ci: remove macos-15-intel

This commit is contained in:
Alexander Medvedev 2026-07-24 12:31:57 +02:00
parent afbe839dea
commit 3103e97e18
116 changed files with 2251 additions and 1470 deletions

View File

@ -23,7 +23,7 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- os: ubuntu-24.04-arm - os: ubuntu-26.04-arm
platform: linux/arm64 platform: linux/arm64
- os: ubuntu-latest - os: ubuntu-latest
platform: linux/amd64 platform: linux/amd64

View File

@ -82,11 +82,10 @@ jobs:
os: os:
[ [
ubuntu-latest, ubuntu-latest,
ubuntu-24.04-arm, ubuntu-26.04-arm,
windows-latest, windows-latest,
windows-11-arm, windows-11-arm,
macos-latest, macos-latest,
macos-15-intel,
] ]
toolchain: toolchain:
- stable - stable
@ -114,7 +113,6 @@ jobs:
windows-latest, windows-latest,
windows-11-arm, windows-11-arm,
macos-latest, macos-latest,
macos-15-intel,
] ]
toolchain: toolchain:
- stable - stable

772
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -119,7 +119,7 @@ tempfile = { version = "3.27.0", default-features = false }
proc-macro-error2 = { version = "2", default-features = false } proc-macro-error2 = { version = "2", default-features = false }
aes = { version = "0.9", default-features = false } aes = { version = "0.9", default-features = false }
async-compression = { version = "0.4.42", default-features = false } async-compression = { version = "0.4.42", default-features = false }
base64 = { version = "0.22.1", default-features = false, features = ["std"] } base64 = { version = "0.23.0", default-features = false, features = ["std"] }
bitflags = { version = "2.13.1", default-features = false, features = ["std"] } bitflags = { version = "2.13.1", default-features = false, features = ["std"] }
cesu8 = { version = "1.1", default-features = false } cesu8 = { version = "1.1", default-features = false }
cfb8 = { version = "0.9", default-features = false } cfb8 = { version = "0.9", default-features = false }
@ -133,7 +133,6 @@ crossfire = { version = "3.1.19", default-features = false, features = ["compat"
crypto-bigint = { version = "0.7.5", default-features = false } crypto-bigint = { version = "0.7.5", default-features = false }
dashmap = { version = "6.2", default-features = false } dashmap = { version = "6.2", default-features = false }
ecdsa = { version = "0.17.0", default-features = false, features = ["std"] } ecdsa = { version = "0.17.0", default-features = false, features = ["std"] }
either = { version = "1.16.0", default-features = false }
enum_dispatch = { version = "0.3.13", default-features = false } enum_dispatch = { version = "0.3.13", default-features = false }
flate2 = { version = "1.1.9", default-features = false, features = ["rust_backend"] } flate2 = { version = "1.1.9", default-features = false, features = ["rust_backend"] }
heck = { version = "0.5", default-features = false } heck = { version = "0.5", default-features = false }
@ -145,7 +144,7 @@ lru = { version = "0.18.1", default-features = false }
lz4-java-wrc = { version = "0.2.0", default-features = false, features = ["lz4_flex"] } lz4-java-wrc = { version = "0.2.0", default-features = false, features = ["lz4_flex"] }
md5 = { version = "0.8", default-features = false } md5 = { version = "0.8", default-features = false }
num-bigint = { version = "0.5", default-features = false, features = ["std"] } num-bigint = { version = "0.5", default-features = false, features = ["std"] }
num-derive = { version = "0.4", default-features = false } num-derive = { version = "0.5", default-features = false }
num-traits = { version = "0.2", default-features = false, features = ["std"] } num-traits = { version = "0.2", default-features = false, features = ["std"] }
p384 = { version = "0.14.0", default-features = false, features = ["std", "arithmetic", "pkcs8", "ecdh"] } p384 = { version = "0.14.0", default-features = false, features = ["std", "arithmetic", "pkcs8", "ecdh"] }
phf = { version = "0.14.0", default-features = false, features = ["std"] } phf = { version = "0.14.0", default-features = false, features = ["std"] }
@ -178,11 +177,9 @@ tracing-subscriber = { version = "0.3.23", default-features = false, features =
"std", "std",
] } ] }
slotmap = { version = "1.1", default-features = false, features = ["std"] } slotmap = { version = "1.1", default-features = false, features = ["std"] }
temp-dir = { version = "0.2.0", default-features = false }
thread_local = { version = "1.1.10", default-features = false }
time = { version = "0.3", default-features = false, features = ["std"] } time = { version = "0.3", default-features = false, features = ["std"] }
arc-swap = { version = "1.9", default-features = false } arc-swap = { version = "1.9", default-features = false }
tokio-util = { version = "0.7.18", default-features = false } tokio-util = { version = "0.7.19", default-features = false }
toml = { version = "1.1", default-features = false, features = ["parse", "display", "serde"] } toml = { version = "1.1", default-features = false, features = ["parse", "display", "serde"] }
ureq = { version = "3.3.0", default-features = false, features = ["rustls"] } ureq = { version = "3.3.0", default-features = false, features = ["rustls"] }
notify = { version = "8.2.0", default-features = false, features = ["macos_fsevent"] } notify = { version = "8.2.0", default-features = false, features = ["macos_fsevent"] }
@ -192,12 +189,12 @@ rustc_version_runtime = { version = "0.3", default-features = false }
ordered-float = { version = "5.3", default-features = false, features = ["std"] } ordered-float = { version = "5.3", default-features = false, features = ["std"] }
xxhash-rust = { version = "0.8", default-features = false, features = ["xxh64"] } xxhash-rust = { version = "0.8", default-features = false, features = ["xxh64"] }
wasmtime = { version = "46.0", default-features = false, features = ["runtime", "component-model", "async", "cache", "cranelift", "gc", "gc-drc", "threads", "std"] } wasmtime = { version = "47.0", default-features = false, features = ["runtime", "component-model", "async", "cache", "cranelift", "gc", "gc-drc", "threads", "std"] }
wasmtime-wasi = { version = "46.0", default-features = false, features = ["p2"] } wasmtime-wasi = { version = "47.0", default-features = false, features = ["p2"] }
wasmtime-wasi-http = { version = "46.0", default-features = false, features = ["p2", "default-send-request"] } wasmtime-wasi-http = { version = "47.0", default-features = false, features = ["p2", "default-send-request"] }
# needed for interacting with wasmtime-wasi-http - keep in sync # needed for interacting with wasmtime-wasi-http - keep in sync
hyper = { version = "^1.10", default-features = false } hyper = { version = "^1.11", default-features = false }
wit-bindgen = { version = "0.59", default-features = false, features = ["macros"] } wit-bindgen = { version = "0.60", default-features = false, features = ["macros"] }
postcard = { version = "1.1", default-features = false, features = ["alloc"] } postcard = { version = "1.1", default-features = false, features = ["alloc"] }
tracing-serde-structured = { version = "0.4", default-features = false } tracing-serde-structured = { version = "0.4", default-features = false }

View File

@ -8,7 +8,6 @@ license.workspace = true
[dependencies] [dependencies]
serde_json.workspace = true serde_json.workspace = true
tracing.workspace = true tracing.workspace = true
either.workspace = true
[lints] [lints]
workspace = true workspace = true

View File

@ -1,5 +1,34 @@
use crate::{DataResult, Decode, DynamicOps, Encode}; use crate::{DataResult, Decode, DynamicOps, Encode};
use either::Either;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Either<L, R> {
Left(L),
Right(R),
}
impl<L, R> Either<L, R> {
pub const fn is_left(&self) -> bool {
matches!(self, Self::Left(_))
}
pub const fn is_right(&self) -> bool {
matches!(self, Self::Right(_))
}
pub fn left(self) -> Option<L> {
match self {
Self::Left(l) => Some(l),
Self::Right(_) => None,
}
}
pub fn right(self) -> Option<R> {
match self {
Self::Left(_) => None,
Self::Right(r) => Some(r),
}
}
}
impl<L: Encode, R: Encode> Encode for Either<L, R> { impl<L: Encode, R: Encode> Encode for Either<L, R> {
fn encode<O: DynamicOps>(&self, ops: &'static O, prefix: O::Value) -> DataResult<O::Value> { fn encode<O: DynamicOps>(&self, ops: &'static O, prefix: O::Value) -> DataResult<O::Value> {
@ -46,9 +75,9 @@ impl<L: Decode, R: Decode> Decode for Either<L, R> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::Either;
use crate::json_ops::JsonOps; use crate::json_ops::JsonOps;
use crate::{assert_decode, assert_encode_success}; use crate::{assert_decode, assert_encode_success};
use either::Either;
use serde_json::json; use serde_json::json;
#[test] #[test]

View File

@ -1,4 +1,4 @@
mod either; pub mod either;
pub mod list; pub mod list;
pub mod map; pub mod map;
pub mod optional_field; pub mod optional_field;

View File

@ -21,6 +21,7 @@ pub use number::Number;
pub use crate::codec::Decode; pub use crate::codec::Decode;
pub use crate::codec::Encode; pub use crate::codec::Encode;
pub use crate::codec::either::Either;
pub use crate::codec::primitive::ByteBuffer; pub use crate::codec::primitive::ByteBuffer;
pub use crate::codec::primitive::IntStream; pub use crate::codec::primitive::IntStream;
pub use crate::codec::primitive::LongStream; pub use crate::codec::primitive::LongStream;

View File

@ -32,6 +32,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b25655df2c3cdd83c5e5b293b88acd880332b2ddadd7c30ac43144fdc0033da9"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
version = "1.8.3" version = "1.8.3"
@ -40,9 +46,9 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.13.0" version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@ -324,7 +330,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@ -584,13 +590,13 @@ dependencies = [
[[package]] [[package]]
name = "num-derive" name = "num-derive"
version = "0.4.2" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" checksum = "e4e98dc3b890f6c23a0f9d3d491a2823d0dea0fa656302a13dd225fa924112a8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 3.0.2",
] ]
[[package]] [[package]]
@ -667,7 +673,7 @@ dependencies = [
"pest_meta", "pest_meta",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@ -723,7 +729,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@ -788,7 +794,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_json5", "serde_json5",
"serde_repr", "serde_repr",
"syn", "syn 3.0.2",
"wit-encoder", "wit-encoder",
] ]
@ -800,6 +806,7 @@ dependencies = [
"cesu8", "cesu8",
"flate2", "flate2",
"pumpkin-codecs", "pumpkin-codecs",
"rustc-hash",
"serde", "serde",
"thiserror", "thiserror",
"tracing", "tracing",
@ -810,7 +817,7 @@ dependencies = [
name = "pumpkin-util" name = "pumpkin-util"
version = "0.1.0-dev+26.2" version = "0.1.0-dev+26.2"
dependencies = [ dependencies = [
"base64", "base64 0.23.0",
"bytes", "bytes",
"colored", "colored",
"crypto-bigint", "crypto-bigint",
@ -828,7 +835,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.11.0", "sha2 0.11.0",
"syn", "syn 3.0.2",
"thiserror", "thiserror",
"tokio", "tokio",
"ureq", "ureq",
@ -918,6 +925,12 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rustc-hash"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.39" version = "0.23.39"
@ -985,9 +998,9 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_derive", "serde_derive",
@ -995,29 +1008,29 @@ dependencies = [
[[package]] [[package]]
name = "serde_core" name = "serde_core"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 3.0.2",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.150" version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"itoa", "itoa",
@ -1046,7 +1059,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@ -1130,6 +1143,17 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "syn"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a207d6d6a2b7fc470b80443726053f18a2481b7e1eee970597051596567987a3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.18" version = "2.0.18"
@ -1147,14 +1171,14 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.52.0" version = "1.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776" checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
dependencies = [ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
@ -1178,7 +1202,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
] ]
[[package]] [[package]]
@ -1226,7 +1250,7 @@ version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"log", "log",
"percent-encoding", "percent-encoding",
"rustls", "rustls",
@ -1242,7 +1266,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"http", "http",
"httparse", "httparse",
"log", "log",
@ -1256,9 +1280,9 @@ checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.23.0" version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
dependencies = [ dependencies = [
"getrandom 0.4.1", "getrandom 0.4.1",
"js-sys", "js-sys",
@ -1329,7 +1353,7 @@ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -1378,9 +1402,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.253.0" version = "0.254.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19db11f87d2486580e1e8b6f494c54df7e0566b87d0b599db843c24019667339" checksum = "d5769a29f799fbab136aaf65b4fe5384cd7d93fe6fc9ba0dcb6c8382a1f16e27"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"indexmap", "indexmap",
@ -1514,7 +1538,7 @@ dependencies = [
"heck", "heck",
"indexmap", "indexmap",
"prettyplease", "prettyplease",
"syn", "syn 2.0.118",
"wasm-metadata", "wasm-metadata",
"wit-bindgen-core", "wit-bindgen-core",
"wit-component", "wit-component",
@ -1530,7 +1554,7 @@ dependencies = [
"prettyplease", "prettyplease",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.118",
"wit-bindgen-core", "wit-bindgen-core",
"wit-bindgen-rust", "wit-bindgen-rust",
] ]
@ -1556,15 +1580,15 @@ dependencies = [
[[package]] [[package]]
name = "wit-encoder" name = "wit-encoder"
version = "0.253.0" version = "0.254.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b783601ee9357f78aaa12bc5cfd0cd0a2fb3d637de1f35604e14eef52ab97d" checksum = "c565f1f3dcf3b659d6c15052739e35937227590d7dded3d3ce274f7bd9c34d93"
dependencies = [ dependencies = [
"id-arena", "id-arena",
"pretty_assertions", "pretty_assertions",
"semver", "semver",
"serde", "serde",
"wit-parser 0.253.0", "wit-parser 0.254.0",
] ]
[[package]] [[package]]
@ -1587,9 +1611,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-parser" name = "wit-parser"
version = "0.253.0" version = "0.254.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d997b8e5920fcbeec742b58e583325d6419a6aca617ae8075c406a61c65ba8a" checksum = "1655131e4f7d3f0cb141f6eca71315ca40eff0f3d4de7cff0a82bacedd8c89b4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hashbrown 0.17.1", "hashbrown 0.17.1",
@ -1601,7 +1625,7 @@ dependencies = [
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"unicode-ident", "unicode-ident",
"wasmparser 0.253.0", "wasmparser 0.254.0",
] ]
[[package]] [[package]]

View File

@ -16,13 +16,13 @@ serde_repr = { version = "0.1" }
serde_json = { version = "1.0", features = ["preserve_order"] } serde_json = { version = "1.0", features = ["preserve_order"] }
serde_json5 = "0.2" serde_json5 = "0.2"
wit-encoder = "0.253.0" wit-encoder = "0.254.0"
semver = "1.0" semver = "1.0"
rayon = "1.12.0" rayon = "1.12.0"
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
syn = { version = "2.0", features = ["full", "extra-traits"] } syn = { version = "3.0", features = ["full", "extra-traits"] }
pumpkin-util = { path = "../pumpkin-util", features = ["codegen"] } pumpkin-util = { path = "../pumpkin-util", features = ["codegen"] }
pumpkin-nbt = { path = "../pumpkin-nbt" } pumpkin-nbt = { path = "../pumpkin-nbt" }

View File

@ -765,7 +765,9 @@ impl ToTokens for ItemComponents {
tokens.extend(quote! { (EnchantmentGlintOverride, &EnchantmentGlintOverrideImpl), }); tokens.extend(quote! { (EnchantmentGlintOverride, &EnchantmentGlintOverrideImpl), });
} }
if self.enchantments.is_some() { if self.enchantments.is_some() {
tokens.extend(quote! { (Enchantments, &EnchantmentsImpl { enchantment: Cow::Borrowed(&[]) }), }); tokens.extend(
quote! { (Enchantments, &EnchantmentsImpl { enchantment: Cow::Borrowed(&[]) }), },
);
} }
if self.entity_data.is_some() { if self.entity_data.is_some() {
tokens.extend(quote! { (EntityData, &EntityDataImpl), }); tokens.extend(quote! { (EntityData, &EntityDataImpl), });
@ -793,7 +795,8 @@ impl ToTokens for ItemComponents {
} }
if let Some(model) = &self.item_model { if let Some(model) = &self.item_model {
let model_lit = LitStr::new(model, Span::call_site()); let model_lit = LitStr::new(model, Span::call_site());
tokens.extend(quote! { (ItemModel, &ItemModelImpl { id: Cow::Borrowed(#model_lit) }), }); tokens
.extend(quote! { (ItemModel, &ItemModelImpl { id: Cow::Borrowed(#model_lit) }), });
} }
if self.kinetic_weapon.is_some() { if self.kinetic_weapon.is_some() {
tokens.extend(quote! { (KineticWeapon, &KineticWeaponImpl), }); tokens.extend(quote! { (KineticWeapon, &KineticWeaponImpl), });
@ -870,7 +873,9 @@ impl ToTokens for ItemComponents {
tokens.extend(quote! { (UseRemainder, &UseRemainderImpl), }); tokens.extend(quote! { (UseRemainder, &UseRemainderImpl), });
} }
if self.writable_book_content.is_some() { if self.writable_book_content.is_some() {
tokens.extend(quote! { (WritableBookContent, &WritableBookContentImpl { pages: Vec::new() }), }); tokens.extend(
quote! { (WritableBookContent, &WritableBookContentImpl { pages: Vec::new() }), },
);
} }
} }
} }

View File

@ -538,7 +538,9 @@ impl ToTokens for LootConditionStruct {
.and_then(|p| p.entity_type.as_ref()) .and_then(|p| p.entity_type.as_ref())
.map(|t| match t { .map(|t| match t {
StringOrVec::String(s) => quote! { Some(#s) }, StringOrVec::String(s) => quote! { Some(#s) },
StringOrVec::Vec(v) => v.first().map_or(quote! { None }, |s| quote! { Some(#s) }), StringOrVec::Vec(v) => {
v.first().map_or(quote! { None }, |s| quote! { Some(#s) })
}
}) })
.unwrap_or(quote! { None }); .unwrap_or(quote! { None });
@ -559,7 +561,9 @@ impl ToTokens for LootConditionStruct {
.and_then(|ench| ench.enchantments.as_ref()) .and_then(|ench| ench.enchantments.as_ref())
.map(|t| match t { .map(|t| match t {
StringOrVec::String(s) => quote! { Some(#s) }, StringOrVec::String(s) => quote! { Some(#s) },
StringOrVec::Vec(v) => v.first().map_or(quote! { None }, |s| quote! { Some(#s) }), StringOrVec::Vec(v) => {
v.first().map_or(quote! { None }, |s| quote! { Some(#s) })
}
}) })
.unwrap_or(quote! { None }); .unwrap_or(quote! { None });
@ -613,7 +617,9 @@ impl ToTokens for LootConditionStruct {
.and_then(|e| e.entity_type.as_ref()) .and_then(|e| e.entity_type.as_ref())
.map(|t| match t { .map(|t| match t {
StringOrVec::String(s) => quote! { Some(#s) }, StringOrVec::String(s) => quote! { Some(#s) },
StringOrVec::Vec(v) => v.first().map_or(quote! { None }, |s| quote! { Some(#s) }), StringOrVec::Vec(v) => {
v.first().map_or(quote! { None }, |s| quote! { Some(#s) })
}
}) })
.unwrap_or(quote! { None }); .unwrap_or(quote! { None });
@ -623,7 +629,9 @@ impl ToTokens for LootConditionStruct {
.and_then(|e| e.entity_type.as_ref()) .and_then(|e| e.entity_type.as_ref())
.map(|t| match t { .map(|t| match t {
StringOrVec::String(s) => quote! { Some(#s) }, StringOrVec::String(s) => quote! { Some(#s) },
StringOrVec::Vec(v) => v.first().map_or(quote! { None }, |s| quote! { Some(#s) }), StringOrVec::Vec(v) => {
v.first().map_or(quote! { None }, |s| quote! { Some(#s) })
}
}) })
.unwrap_or(quote! { None }); .unwrap_or(quote! { None });
@ -643,7 +651,9 @@ impl ToTokens for LootConditionStruct {
.and_then(|p| p.biome.as_ref()) .and_then(|p| p.biome.as_ref())
.map(|b| match b { .map(|b| match b {
StringOrVec::String(s) => quote! { Some(#s) }, StringOrVec::String(s) => quote! { Some(#s) },
StringOrVec::Vec(v) => v.first().map_or(quote! { None }, |s| quote! { Some(#s) }), StringOrVec::Vec(v) => {
v.first().map_or(quote! { None }, |s| quote! { Some(#s) })
}
}) })
.unwrap_or(quote! { None }); .unwrap_or(quote! { None });
quote! { LootCondition::LocationCheck { offset_x: #ox, offset_y: #oy, offset_z: #oz, expected_biome: #expected_biome } } quote! { LootCondition::LocationCheck { offset_x: #ox, offset_y: #oy, offset_z: #oz, expected_biome: #expected_biome } }

View File

@ -165,7 +165,7 @@ fn collect_fields(
.collect() .collect()
} }
fn wit_name(name: String,state: &str) -> String{ fn wit_name(name: String, state: &str) -> String {
if state == "play" { if state == "play" {
name.to_kebab_case() name.to_kebab_case()
} else { } else {
@ -180,7 +180,7 @@ fn process_struct(
variant: &mut Variant, variant: &mut Variant,
defined_cases: &mut HashSet<String>, defined_cases: &mut HashSet<String>,
) { ) {
let wit_name = wit_name(s.ident.to_string(),state); let wit_name = wit_name(s.ident.to_string(), state);
if !defined_cases.insert(wit_name.clone()) { if !defined_cases.insert(wit_name.clone()) {
return; return;
} }
@ -199,7 +199,7 @@ fn process_enum(
variant: &mut Variant, variant: &mut Variant,
defined_cases: &mut HashSet<String>, defined_cases: &mut HashSet<String>,
) { ) {
let enum_wit_name = wit_name(e.ident.to_string(),state); let enum_wit_name = wit_name(e.ident.to_string(), state);
if !defined_cases.insert(enum_wit_name.clone()) { if !defined_cases.insert(enum_wit_name.clone()) {
return; return;
} }

View File

@ -43,7 +43,7 @@ pub fn build_java_mapping() -> String {
output.push_str("}\n\n"); output.push_str("}\n\n");
output.push_str("#[must_use]\n"); output.push_str("#[must_use]\n");
output.push_str("pub fn deserialize_java_serverbound_packet(id: i32, payload: &[u8], version: JavaMinecraftVersion) -> Option<ServerboundPacket> {\n"); output.push_str("pub fn deserialize_java_serverbound_packet(id: i32, mut payload: &[u8], version: JavaMinecraftVersion) -> Option<ServerboundPacket> {\n");
output.push_str(" match id {\n"); output.push_str(" match id {\n");
for state in server_states { for state in server_states {
@ -715,7 +715,7 @@ fn emit_struct_output(
)); ));
output.push_str(" use pumpkin_protocol::ServerPacket;\n"); output.push_str(" use pumpkin_protocol::ServerPacket;\n");
output.push_str(&format!( output.push_str(&format!(
" let p = <{}::{} as pumpkin_protocol::ServerPacket>::read(&mut Cursor::new(payload), &version).ok()?;\n", " let p = <{}::{} as pumpkin_protocol::ServerPacket>::read(&mut payload, &version).ok()?;\n",
rust_path_prefix, struct_name rust_path_prefix, struct_name
)); ));
} else { } else {
@ -846,7 +846,7 @@ fn process_enum(
)); ));
output.push_str(" use pumpkin_protocol::ServerPacket;\n"); output.push_str(" use pumpkin_protocol::ServerPacket;\n");
output.push_str(&format!( output.push_str(&format!(
" let p = <{}::{} as pumpkin_protocol::ServerPacket>::read(&mut Cursor::new(payload), &version).ok()?;\n", " let p = <{}::{} as pumpkin_protocol::ServerPacket>::read(&mut payload, &version).ok()?;\n",
rust_path_prefix, enum_name rust_path_prefix, enum_name
)); ));
} else { } else {

View File

@ -106,7 +106,7 @@ pub fn bench_nbt(c: &mut Criterion) {
c.bench_function("nbt/java/deserialize/raw", |b| { c.bench_function("nbt/java/deserialize/raw", |b| {
b.iter(|| { b.iter(|| {
let mut cursor = Cursor::new(&wrapper_bytes_java); let mut cursor = Cursor::new(&wrapper_bytes_java[..]);
let mut reader = deserializer::NbtReadHelperJava::new(&mut cursor); let mut reader = deserializer::NbtReadHelperJava::new(&mut cursor);
Nbt::read(&mut reader).unwrap(); Nbt::read(&mut reader).unwrap();
}); });
@ -135,7 +135,7 @@ pub fn bench_nbt(c: &mut Criterion) {
c.bench_function("nbt/bedrock/deserialize/raw", |b| { c.bench_function("nbt/bedrock/deserialize/raw", |b| {
b.iter(|| { b.iter(|| {
let mut cursor = Cursor::new(&wrapper_bytes_bedrock); let mut cursor = Cursor::new(&wrapper_bytes_bedrock[..]);
let mut reader = deserializer::NbtReadHelperBedrock::new(&mut cursor); let mut reader = deserializer::NbtReadHelperBedrock::new(&mut cursor);
Nbt::read(&mut reader).unwrap(); Nbt::read(&mut reader).unwrap();
}); });

View File

@ -43,7 +43,7 @@ impl NbtCompound {
} }
} }
pub fn skip_content<R: NbtReadHelper>(reader: &mut R) -> Result<(), Error> { pub fn skip_content<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<(), Error> {
loop { loop {
let tag_id = match reader.get_u8() { let tag_id = match reader.get_u8() {
Ok(id) => id, Ok(id) => id,
@ -64,7 +64,7 @@ impl NbtCompound {
Ok(()) Ok(())
} }
pub fn deserialize_content<R: NbtReadHelper>(reader: &mut R) -> Result<Self, Error> { pub fn deserialize_content<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<Self, Error> {
let mut compound = Self::new(); let mut compound = Self::new();
loop { loop {

View File

@ -1,5 +1,6 @@
use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::io::{Seek, SeekFrom}; use std::io::{Cursor, Seek, SeekFrom};
use crate::{ use crate::{
BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, END_ID, Error, INT_ARRAY_ID, INT_ID, LIST_ID, BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, END_ID, Error, INT_ARRAY_ID, INT_ID, LIST_ID,
@ -25,16 +26,178 @@ pub(super) fn set_curr_visitor_seq_list_id(tag: Option<u8>) {
}); });
} }
pub trait NbtReadHelper { pub trait NbtDataSource<'a> {
type Reader: Read + Seek; fn read_u8(&mut self) -> Result<u8>;
fn read_bytes(&mut self, buf: &mut [u8]) -> Result<()>;
fn seek_relative(&mut self, offset: i64) -> Result<()>;
fn read_string(&mut self, len: usize) -> Result<Cow<'a, str>>;
fn read_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>>;
}
pub struct NbtStreamReader<R>(pub R);
impl<'a, R: Read + Seek> NbtDataSource<'a> for NbtStreamReader<R> {
fn read_u8(&mut self) -> Result<u8> {
let mut buf = [0u8; 1];
self.0.read_exact(&mut buf).map_err(Error::Incomplete)?;
Ok(buf[0])
}
fn read_bytes(&mut self, buf: &mut [u8]) -> Result<()> {
self.0.read_exact(buf).map_err(Error::Incomplete)
}
fn seek_relative(&mut self, offset: i64) -> Result<()> {
self.0
.seek(SeekFrom::Current(offset))
.map_err(Error::Incomplete)?;
Ok(())
}
fn read_string(&mut self, len: usize) -> Result<Cow<'a, str>> {
let mut buf = vec![0u8; len];
self.0.read_exact(&mut buf).map_err(Error::Incomplete)?;
let string = cesu8::from_java_cesu8(&buf).map_err(|_| Error::Cesu8DecodingError)?;
Ok(Cow::Owned(string.into_owned()))
}
fn read_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>> {
let mut buf = vec![0u8; len];
self.0.read_exact(&mut buf).map_err(Error::Incomplete)?;
let i8_buf: Vec<i8> = buf.into_iter().map(|b| b as i8).collect();
Ok(Cow::Owned(i8_buf))
}
}
impl<'a> NbtDataSource<'a> for Cursor<&'a [u8]> {
fn read_u8(&mut self) -> Result<u8> {
let mut buf = [0u8; 1];
self.read_exact(&mut buf).map_err(Error::Incomplete)?;
Ok(buf[0])
}
fn read_bytes(&mut self, buf: &mut [u8]) -> Result<()> {
self.read_exact(buf).map_err(Error::Incomplete)
}
fn seek_relative(&mut self, offset: i64) -> Result<()> {
self.seek(SeekFrom::Current(offset))
.map_err(Error::Incomplete)?;
Ok(())
}
fn read_string(&mut self, len: usize) -> Result<Cow<'a, str>> {
let pos = self.position() as usize;
let data_len = self.get_ref().len();
if pos + len > data_len {
return Err(Error::Incomplete(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF",
)));
}
self.set_position((pos + len) as u64);
let data = self.get_ref();
let slice = &data[pos..pos + len];
if let Ok(s) = std::str::from_utf8(slice) {
Ok(Cow::Borrowed(s))
} else {
let string = cesu8::from_java_cesu8(slice).map_err(|_| Error::Cesu8DecodingError)?;
Ok(string)
}
}
fn read_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>> {
let pos = self.position() as usize;
let data_len = self.get_ref().len();
if pos + len > data_len {
return Err(Error::Incomplete(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF",
)));
}
self.set_position((pos + len) as u64);
let data = self.get_ref();
let slice = &data[pos..pos + len];
let i8_slice = unsafe { std::slice::from_raw_parts(slice.as_ptr().cast::<i8>(), len) };
Ok(Cow::Borrowed(i8_slice))
}
}
impl<'a, S: NbtDataSource<'a> + ?Sized> NbtDataSource<'a> for &mut S {
fn read_u8(&mut self) -> Result<u8> {
(**self).read_u8()
}
fn read_bytes(&mut self, buf: &mut [u8]) -> Result<()> {
(**self).read_bytes(buf)
}
fn seek_relative(&mut self, offset: i64) -> Result<()> {
(**self).seek_relative(offset)
}
fn read_string(&mut self, len: usize) -> Result<Cow<'a, str>> {
(**self).read_string(len)
}
fn read_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>> {
(**self).read_byte_array(len)
}
}
impl<'a> NbtDataSource<'a> for Cursor<Vec<u8>> {
fn read_u8(&mut self) -> Result<u8> {
let mut buf = [0u8; 1];
self.read_exact(&mut buf).map_err(Error::Incomplete)?;
Ok(buf[0])
}
fn read_bytes(&mut self, buf: &mut [u8]) -> Result<()> {
self.read_exact(buf).map_err(Error::Incomplete)
}
fn seek_relative(&mut self, offset: i64) -> Result<()> {
self.seek(SeekFrom::Current(offset))
.map_err(Error::Incomplete)?;
Ok(())
}
fn read_string(&mut self, len: usize) -> Result<Cow<'a, str>> {
let pos = self.position() as usize;
let data_len = self.get_ref().len();
if pos + len > data_len {
return Err(Error::Incomplete(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF",
)));
}
self.set_position((pos + len) as u64);
let data = self.get_ref();
let slice = &data[pos..pos + len];
let string = cesu8::from_java_cesu8(slice).map_err(|_| Error::Cesu8DecodingError)?;
Ok(Cow::Owned(string.into_owned()))
}
fn read_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>> {
let pos = self.position() as usize;
let data_len = self.get_ref().len();
if pos + len > data_len {
return Err(Error::Incomplete(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF",
)));
}
self.set_position((pos + len) as u64);
let data = self.get_ref();
let slice = &data[pos..pos + len];
let i8_slice = unsafe { std::slice::from_raw_parts(slice.as_ptr().cast::<i8>(), len) };
Ok(Cow::Owned(i8_slice.to_vec()))
}
}
pub trait NbtReadHelper<'a> {
type Reader: NbtDataSource<'a>;
fn reader(&mut self) -> &mut Self::Reader; fn reader(&mut self) -> &mut Self::Reader;
fn skip_bytes(&mut self, count: i64) -> Result<()> { fn skip_bytes(&mut self, count: i64) -> Result<()> {
self.reader() self.reader().seek_relative(count)
.seek(SeekFrom::Current(count))
.map_err(Error::Incomplete)?;
Ok(())
} }
fn skip_u8(&mut self) -> Result<()> { fn skip_u8(&mut self) -> Result<()> {
self.skip_bytes(1) self.skip_bytes(1)
@ -66,63 +229,42 @@ pub trait NbtReadHelper {
fn get_i64(&mut self) -> Result<i64>; fn get_i64(&mut self) -> Result<i64>;
fn get_f32(&mut self) -> Result<f32>; fn get_f32(&mut self) -> Result<f32>;
fn get_f64(&mut self) -> Result<f64>; fn get_f64(&mut self) -> Result<f64>;
fn get_string(&mut self) -> Result<String>; fn get_string(&mut self) -> Result<Cow<'a, str>>;
fn get_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>>;
} }
macro_rules! define_get_number_be { pub struct NbtReadHelperJava<D> {
($name:ident, $type:ty) => { reader: D,
fn $name(&mut self) -> Result<$type> {
let mut buf = [0u8; std::mem::size_of::<$type>()];
self.reader
.read_exact(&mut buf)
.map_err(Error::Incomplete)?;
Ok(<$type>::from_be_bytes(buf))
}
};
} }
macro_rules! define_get_number_le { impl<D> NbtReadHelperJava<D> {
($name:ident, $type:ty) => { pub const fn new(r: D) -> Self {
fn $name(&mut self) -> Result<$type> {
let mut buf = [0u8; std::mem::size_of::<$type>()];
self.reader
.read_exact(&mut buf)
.map_err(Error::Incomplete)?;
Ok(<$type>::from_le_bytes(buf))
}
};
}
pub struct NbtReadHelperJava<R: Read + Seek> {
reader: R,
}
impl<R: Read + Seek> NbtReadHelperJava<R> {
pub const fn new(r: R) -> Self {
Self { reader: r } Self { reader: r }
} }
} }
pub struct NbtReadHelperBedrock<R: Read + Seek> { pub struct NbtReadHelperBedrock<D> {
reader: R, reader: D,
} }
impl<R: Read + Seek> NbtReadHelperBedrock<R> { impl<D> NbtReadHelperBedrock<D> {
pub const fn new(r: R) -> Self { pub const fn new(r: D) -> Self {
Self { reader: r } Self { reader: r }
} }
} }
impl<R: Read + Seek> NbtReadHelperJava<R> { impl<'a, D: NbtDataSource<'a>> NbtReadHelperJava<D> {
define_get_number_be!(get_string_len, u16); fn get_string_len(&mut self) -> Result<u16> {
let mut buf = [0u8; 2];
self.reader.read_bytes(&mut buf)?;
Ok(u16::from_be_bytes(buf))
}
} }
impl<R: Read + Seek> NbtReadHelper for NbtReadHelperJava<R> { impl<'a, D: NbtDataSource<'a>> NbtReadHelper<'a> for NbtReadHelperJava<D> {
type Reader = R; type Reader = D;
fn reader(&mut self) -> &mut R { fn reader(&mut self) -> &mut D {
&mut self.reader &mut self.reader
} }
@ -131,31 +273,54 @@ impl<R: Read + Seek> NbtReadHelper for NbtReadHelperJava<R> {
self.skip_bytes(len) self.skip_bytes(len)
} }
define_get_number_be!(get_u8, u8); fn get_u8(&mut self) -> Result<u8> {
define_get_number_be!(get_i8, i8); self.reader.read_u8()
define_get_number_be!(get_i16, i16); }
define_get_number_be!(get_i32, i32); fn get_i8(&mut self) -> Result<i8> {
define_get_number_be!(get_i64, i64); Ok(self.reader.read_u8()? as i8)
define_get_number_be!(get_f32, f32); }
define_get_number_be!(get_f64, f64); fn get_i16(&mut self) -> Result<i16> {
let mut buf = [0u8; 2];
self.reader.read_bytes(&mut buf)?;
Ok(i16::from_be_bytes(buf))
}
fn get_i32(&mut self) -> Result<i32> {
let mut buf = [0u8; 4];
self.reader.read_bytes(&mut buf)?;
Ok(i32::from_be_bytes(buf))
}
fn get_i64(&mut self) -> Result<i64> {
let mut buf = [0u8; 8];
self.reader.read_bytes(&mut buf)?;
Ok(i64::from_be_bytes(buf))
}
fn get_f32(&mut self) -> Result<f32> {
let mut buf = [0u8; 4];
self.reader.read_bytes(&mut buf)?;
Ok(f32::from_be_bytes(buf))
}
fn get_f64(&mut self) -> Result<f64> {
let mut buf = [0u8; 8];
self.reader.read_bytes(&mut buf)?;
Ok(f64::from_be_bytes(buf))
}
fn get_string(&mut self) -> Result<String> { fn get_string(&mut self) -> Result<Cow<'a, str>> {
let len = self.get_string_len()? as usize; let len = self.get_string_len()? as usize;
self.reader.read_string(len)
}
let mut buf = vec![0u8; len]; fn get_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>> {
self.reader self.reader.read_byte_array(len)
.read_exact(&mut buf)
.map_err(Error::Incomplete)?;
let string = cesu8::from_java_cesu8(&buf).map_err(|_| Error::Cesu8DecodingError)?;
Ok(string.into_owned())
} }
} }
impl<R: Read + Seek> NbtReadHelperBedrock<R> { impl<'a, D: NbtDataSource<'a>> NbtReadHelperBedrock<D> {
fn get_u8(&mut self) -> Result<u8> {
self.reader.read_u8()
}
fn get_var_u32(&mut self) -> Result<u32> { fn get_var_u32(&mut self) -> Result<u32> {
// LEB128
let mut val = 0; let mut val = 0;
for i in 0..5 { for i in 0..5 {
let byte = self.get_u8()?; let byte = self.get_u8()?;
@ -169,12 +334,10 @@ impl<R: Read + Seek> NbtReadHelperBedrock<R> {
fn get_var_i32(&mut self) -> Result<i32> { fn get_var_i32(&mut self) -> Result<i32> {
let val = self.get_var_u32()?; let val = self.get_var_u32()?;
// ZigZag
Ok(((val >> 1) as i32) ^ -((val as i32) & 1)) Ok(((val >> 1) as i32) ^ -((val as i32) & 1))
} }
fn get_var_u64(&mut self) -> Result<u64> { fn get_var_u64(&mut self) -> Result<u64> {
// LEB128
let mut val = 0; let mut val = 0;
for i in 0..10 { for i in 0..10 {
let byte = self.get_u8()?; let byte = self.get_u8()?;
@ -188,7 +351,6 @@ impl<R: Read + Seek> NbtReadHelperBedrock<R> {
fn get_var_i64(&mut self) -> Result<i64> { fn get_var_i64(&mut self) -> Result<i64> {
let val = self.get_var_u64()?; let val = self.get_var_u64()?;
// ZigZag
Ok(((val >> 1) as i64) ^ -((val as i64) & 1)) Ok(((val >> 1) as i64) ^ -((val as i64) & 1))
} }
@ -197,67 +359,64 @@ impl<R: Read + Seek> NbtReadHelperBedrock<R> {
} }
} }
impl<R: Read + Seek> NbtReadHelper for NbtReadHelperBedrock<R> { impl<'a, D: NbtDataSource<'a>> NbtReadHelper<'a> for NbtReadHelperBedrock<D> {
type Reader = R; type Reader = D;
fn reader(&mut self) -> &mut Self::Reader { fn reader(&mut self) -> &mut D {
&mut self.reader &mut self.reader
} }
fn skip_i32(&mut self) -> Result<()> {
for _ in 0..5 {
if self.get_u8()? & 0x80 == 0 {
return Ok(());
}
}
Err(Error::VarIntTooLarge)
}
fn skip_i64(&mut self) -> Result<()> {
for _ in 0..10 {
if self.get_u8()? & 0x80 == 0 {
return Ok(());
}
}
Err(Error::VarLongTooLarge)
}
fn skip_string(&mut self) -> Result<()> { fn skip_string(&mut self) -> Result<()> {
let len = self.get_string_len()? as i64; let len = self.get_string_len()? as i64;
self.skip_bytes(len) self.skip_bytes(len)
} }
define_get_number_le!(get_u8, u8); fn get_u8(&mut self) -> Result<u8> {
define_get_number_le!(get_i8, i8); self.reader.read_u8()
define_get_number_le!(get_i16, i16); }
fn get_i8(&mut self) -> Result<i8> {
Ok(self.reader.read_u8()? as i8)
}
fn get_i16(&mut self) -> Result<i16> {
let mut buf = [0u8; 2];
self.reader.read_bytes(&mut buf)?;
Ok(i16::from_le_bytes(buf))
}
fn get_i32(&mut self) -> Result<i32> { fn get_i32(&mut self) -> Result<i32> {
self.get_var_i32() self.get_var_i32()
} }
fn get_i64(&mut self) -> Result<i64> { fn get_i64(&mut self) -> Result<i64> {
self.get_var_i64() self.get_var_i64()
} }
define_get_number_le!(get_f32, f32); fn get_f32(&mut self) -> Result<f32> {
define_get_number_le!(get_f64, f64); let mut buf = [0u8; 4];
self.reader.read_bytes(&mut buf)?;
Ok(f32::from_le_bytes(buf))
}
fn get_f64(&mut self) -> Result<f64> {
let mut buf = [0u8; 8];
self.reader.read_bytes(&mut buf)?;
Ok(f64::from_le_bytes(buf))
}
fn get_string(&mut self) -> Result<String> { fn get_string(&mut self) -> Result<Cow<'a, str>> {
let len = self.get_string_len()? as usize; let len = self.get_string_len()? as usize;
self.reader.read_string(len)
}
let mut buf = vec![0u8; len]; fn get_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>> {
self.reader self.reader.read_byte_array(len)
.read_exact(&mut buf)
.map_err(Error::Incomplete)?;
String::from_utf8(buf).map_err(|_| Error::Utf8DecodingError)
} }
} }
pub struct Deserializer<R: NbtReadHelper> { pub struct Deserializer<R> {
input: R, input: R,
tag_to_deserialize_stack: Option<u8>, tag_to_deserialize_stack: Option<u8>,
// Yes, this breaks with recursion. Just an attempt at a sanity check
in_list: bool, in_list: bool,
is_named: bool, is_named: bool,
} }
impl<R: NbtReadHelper> Deserializer<R> { impl<R> Deserializer<R> {
pub const fn new(input: R, is_named: bool) -> Self { pub const fn new(input: R, is_named: bool) -> Self {
Self { Self {
input, input,
@ -270,28 +429,46 @@ impl<R: NbtReadHelper> Deserializer<R> {
/// Deserializes struct using Serde Deserializer from normal NBT /// Deserializes struct using Serde Deserializer from normal NBT
pub fn from_bytes<'a, T: Deserialize<'a>>(r: impl Read + Seek) -> Result<T> { pub fn from_bytes<'a, T: Deserialize<'a>>(r: impl Read + Seek) -> Result<T> {
let mut deserializer = Deserializer::new(NbtReadHelperJava::new(r), true); let mut deserializer = Deserializer::new(NbtReadHelperJava::new(NbtStreamReader(r)), true);
T::deserialize(&mut deserializer) T::deserialize(&mut deserializer)
} }
/// Deserializes struct using Serde Deserializer from network NBT /// Deserializes struct using Serde Deserializer from network NBT
pub fn from_bytes_unnamed<'a, T: Deserialize<'a>>(r: impl Read + Seek) -> Result<T> { pub fn from_bytes_unnamed<'a, T: Deserialize<'a>>(r: impl Read + Seek) -> Result<T> {
let mut deserializer = Deserializer::new(NbtReadHelperJava::new(r), false); let mut deserializer = Deserializer::new(NbtReadHelperJava::new(NbtStreamReader(r)), false);
T::deserialize(&mut deserializer) T::deserialize(&mut deserializer)
} }
/// Deserializes struct using Serde Deserializer from Bedrock network NBT /// Deserializes struct using Serde Deserializer from Bedrock network NBT
pub fn from_bytes_bedrock<'a, T: Deserialize<'a>>(r: impl Read + Seek) -> Result<T> { pub fn from_bytes_bedrock<'a, T: Deserialize<'a>>(r: impl Read + Seek) -> Result<T> {
let mut deserializer = Deserializer::new(NbtReadHelperBedrock::new(r), true); let mut deserializer = Deserializer::new(NbtReadHelperBedrock::new(NbtStreamReader(r)), true);
T::deserialize(&mut deserializer) T::deserialize(&mut deserializer)
} }
impl<'de, R: NbtReadHelper> de::Deserializer<'de> for &mut Deserializer<R> { /// Deserializes struct using Serde Deserializer from a normal NBT slice (zero-allocation)
pub fn from_slice<'a, T: Deserialize<'a>>(slice: &'a [u8]) -> Result<T> {
let mut deserializer = Deserializer::new(NbtReadHelperJava::new(Cursor::new(slice)), true);
T::deserialize(&mut deserializer)
}
/// Deserializes struct using Serde Deserializer from a network NBT slice (zero-allocation)
pub fn from_slice_unnamed<'a, T: Deserialize<'a>>(slice: &'a [u8]) -> Result<T> {
let mut deserializer = Deserializer::new(NbtReadHelperJava::new(Cursor::new(slice)), false);
T::deserialize(&mut deserializer)
}
/// Deserializes struct using Serde Deserializer from a Bedrock network NBT slice (zero-allocation)
pub fn from_slice_bedrock<'a, T: Deserialize<'a>>(slice: &'a [u8]) -> Result<T> {
let mut deserializer = Deserializer::new(NbtReadHelperBedrock::new(Cursor::new(slice)), true);
T::deserialize(&mut deserializer)
}
impl<'de, R: NbtReadHelper<'de>> de::Deserializer<'de> for &mut Deserializer<R> {
type Error = Error; type Error = Error;
forward_to_deserialize_any! { forward_to_deserialize_any! {
i8 i16 i32 i64 f32 f64 char str string unit unit_struct seq tuple tuple_struct i8 i16 i32 i64 f32 f64 char unit unit_struct seq tuple tuple_struct
bytes newtype_struct byte_buf newtype_struct byte_buf
} }
fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
@ -334,8 +511,6 @@ impl<'de, R: NbtReadHelper> de::Deserializer<'de> for &mut Deserializer<R> {
return Err(Error::LargeLength(remaining_values)); return Err(Error::LargeLength(remaining_values));
} }
//TODO this is a bit hacky but I couldn't think of a better way
// This flag gets auto cleared in visit_seq
set_curr_visitor_seq_list_id(Some(list_type)); set_curr_visitor_seq_list_id(Some(list_type));
let result = visitor.visit_seq(ListAccess { let result = visitor.visit_seq(ListAccess {
de: self, de: self,
@ -353,7 +528,7 @@ impl<'de, R: NbtReadHelper> de::Deserializer<'de> for &mut Deserializer<R> {
NbtTag::Long(value) => visitor.visit_i64::<Error>(value)?, NbtTag::Long(value) => visitor.visit_i64::<Error>(value)?,
NbtTag::Float(value) => visitor.visit_f32::<Error>(value)?, NbtTag::Float(value) => visitor.visit_f32::<Error>(value)?,
NbtTag::Double(value) => visitor.visit_f64::<Error>(value)?, NbtTag::Double(value) => visitor.visit_f64::<Error>(value)?,
NbtTag::String(value) => visitor.visit_string::<Error>(value.into())?, NbtTag::String(value) => visitor.visit_string::<Error>(value.into_string())?,
_ => return Err(Error::SerdeError("Unreachable state reached".to_string())), _ => return Err(Error::SerdeError("Unreachable state reached".to_string())),
}; };
Ok(result) Ok(result)
@ -378,13 +553,11 @@ impl<'de, R: NbtReadHelper> de::Deserializer<'de> for &mut Deserializer<R> {
} }
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// ZigZag might make this a problem for Bedrock...
let value = self.input.get_i32()?; let value = self.input.get_i32()?;
visitor.visit_i32::<Error>(value) visitor.visit_i32::<Error>(value)
} }
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// ZigZag might make this a problem for Bedrock...
let value = self.input.get_i64()?; let value = self.input.get_i64()?;
visitor.visit_i64::<Error>(value) visitor.visit_i64::<Error>(value)
} }
@ -399,18 +572,49 @@ impl<'de, R: NbtReadHelper> de::Deserializer<'de> for &mut Deserializer<R> {
visitor.visit_bool(false) visitor.visit_bool(false)
} }
fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
match self.input.get_string()? {
Cow::Borrowed(s) => visitor.visit_borrowed_str(s),
Cow::Owned(s) => visitor.visit_string(s),
}
}
fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
self.deserialize_str(visitor)
}
fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
let tag_id = self.tag_to_deserialize_stack.unwrap_or(BYTE_ARRAY_ID);
if tag_id == BYTE_ARRAY_ID {
let len = self.input.get_i32()?;
if len < 0 {
return Err(Error::NegativeLength(len));
}
let slice = self.input.get_byte_array(len as usize)?;
let u8_slice: &'de [u8] =
unsafe { std::slice::from_raw_parts(slice.as_ptr().cast::<u8>(), slice.len()) };
match slice {
Cow::Borrowed(_) => visitor.visit_borrowed_bytes(u8_slice),
Cow::Owned(_) => visitor.visit_bytes(u8_slice),
}
} else {
self.deserialize_any(visitor)
}
}
fn deserialize_enum<V: Visitor<'de>>( fn deserialize_enum<V: Visitor<'de>>(
self, self,
_name: &'static str, _name: &'static str,
_variants: &'static [&'static str], _variants: &'static [&'static str],
visitor: V, visitor: V,
) -> Result<V::Value> { ) -> Result<V::Value> {
let variant = self.input.get_string()?; match self.input.get_string()? {
visitor.visit_enum(variant.into_deserializer()) Cow::Borrowed(s) => visitor.visit_enum(s.into_deserializer()),
Cow::Owned(s) => visitor.visit_enum(s.into_deserializer()),
}
} }
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
// None is not encoded, so no need for it
visitor.visit_some(self) visitor.visit_some(self)
} }
@ -446,8 +650,7 @@ impl<'de, R: NbtReadHelper> de::Deserializer<'de> for &mut Deserializer<R> {
} }
fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
let str = self.input.get_string()?; self.deserialize_str(visitor)
visitor.visit_string(str)
} }
fn is_human_readable(&self) -> bool { fn is_human_readable(&self) -> bool {
@ -455,11 +658,11 @@ impl<'de, R: NbtReadHelper> de::Deserializer<'de> for &mut Deserializer<R> {
} }
} }
struct CompoundAccess<'a, R: NbtReadHelper> { struct CompoundAccess<'a, R> {
de: &'a mut Deserializer<R>, de: &'a mut Deserializer<R>,
} }
impl<'de, R: NbtReadHelper> MapAccess<'de> for CompoundAccess<'_, R> { impl<'de, R: NbtReadHelper<'de>> MapAccess<'de> for CompoundAccess<'_, R> {
type Error = Error; type Error = Error;
fn next_key_seed<K: DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>> { fn next_key_seed<K: DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>> {
@ -478,16 +681,18 @@ impl<'de, R: NbtReadHelper> MapAccess<'de> for CompoundAccess<'_, R> {
} }
} }
struct MapKey<'a, R: NbtReadHelper> { struct MapKey<'a, R> {
de: &'a mut Deserializer<R>, de: &'a mut Deserializer<R>,
} }
impl<'de, R: NbtReadHelper> de::Deserializer<'de> for MapKey<'_, R> { impl<'de, R: NbtReadHelper<'de>> de::Deserializer<'de> for MapKey<'_, R> {
type Error = Error; type Error = Error;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> { fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
let key = self.de.input.get_string()?; match self.de.input.get_string()? {
visitor.visit_string(key) Cow::Borrowed(s) => visitor.visit_borrowed_str(s),
Cow::Owned(s) => visitor.visit_string(s),
}
} }
forward_to_deserialize_any! { forward_to_deserialize_any! {
@ -496,13 +701,13 @@ impl<'de, R: NbtReadHelper> de::Deserializer<'de> for MapKey<'_, R> {
} }
} }
struct ListAccess<'a, R: NbtReadHelper> { struct ListAccess<'a, R> {
de: &'a mut Deserializer<R>, de: &'a mut Deserializer<R>,
remaining_values: usize, remaining_values: usize,
list_type: u8, list_type: u8,
} }
impl<'de, R: NbtReadHelper> SeqAccess<'de> for ListAccess<'_, R> { impl<'de, R: NbtReadHelper<'de>> SeqAccess<'de> for ListAccess<'_, R> {
type Error = Error; type Error = Error;
fn size_hint(&self) -> Option<usize> { fn size_hint(&self) -> Option<usize> {

View File

@ -19,7 +19,10 @@ pub mod serializer;
pub mod tag; pub mod tag;
pub use compound::NbtCompound; pub use compound::NbtCompound;
pub use deserializer::{from_bytes, from_bytes_unnamed}; pub use deserializer::{
from_bytes, from_bytes_bedrock, from_bytes_unnamed, from_slice, from_slice_bedrock,
from_slice_unnamed,
};
pub use serializer::{to_bytes, to_bytes_named, to_bytes_unnamed}; pub use serializer::{to_bytes, to_bytes_named, to_bytes_unnamed};
// This NBT crate is inspired from CrabNBT // This NBT crate is inspired from CrabNBT
@ -93,7 +96,7 @@ impl Nbt {
} }
} }
pub fn read<R: NbtReadHelper>(reader: &mut R) -> Result<Self, Error> { pub fn read<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<Self, Error> {
let tag_type_id = reader.get_u8()?; let tag_type_id = reader.get_u8()?;
if tag_type_id != COMPOUND_ID { if tag_type_id != COMPOUND_ID {
@ -101,13 +104,13 @@ impl Nbt {
} }
Ok(Self { Ok(Self {
name: reader.get_string()?, name: reader.get_string()?.into_owned(),
root_tag: NbtCompound::deserialize_content(reader)?, root_tag: NbtCompound::deserialize_content(reader)?,
}) })
} }
/// Reads an NBT tag that doesn't contain the name of the root `Compound`. /// Reads an NBT tag that doesn't contain the name of the root `Compound`.
pub fn read_unnamed<R: NbtReadHelper>(reader: &mut R) -> Result<Self, Error> { pub fn read_unnamed<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<Self, Error> {
let tag_type_id = reader.get_u8()?; let tag_type_id = reader.get_u8()?;
if tag_type_id != COMPOUND_ID { if tag_type_id != COMPOUND_ID {
@ -256,6 +259,31 @@ mod test {
string: String, string: String,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct BorrowedTest<'a> {
byte: i8,
string: &'a str,
}
#[test]
fn zero_alloc_from_slice() {
let test = Test {
byte: 123,
short: 1342,
int: 4313,
long: 34,
float: 1.00,
string: "Zero alloc NBT".to_string(),
};
let mut bytes = Vec::new();
to_bytes_unnamed(&test, &mut bytes).unwrap();
let borrowed: BorrowedTest = crate::from_slice_unnamed(&bytes).unwrap();
assert_eq!(borrowed.byte, 123);
assert_eq!(borrowed.string, "Zero alloc NBT");
}
#[test] #[test]
fn simple_ser_de_unnamed() { fn simple_ser_de_unnamed() {
let test = Test { let test = Test {

View File

@ -175,12 +175,12 @@ impl NbtTag {
Ok(()) Ok(())
} }
pub fn deserialize<R: NbtReadHelper>(reader: &mut R) -> Result<Self, Error> { pub fn deserialize<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<Self, Error> {
let tag_id = reader.get_u8()?; let tag_id = reader.get_u8()?;
Self::deserialize_data(reader, tag_id) Self::deserialize_data(reader, tag_id)
} }
pub fn skip_data<R: NbtReadHelper>(reader: &mut R, tag_id: u8) -> Result<(), Error> { pub fn skip_data<'a, R: NbtReadHelper<'a>>(reader: &mut R, tag_id: u8) -> Result<(), Error> {
match tag_id { match tag_id {
END_ID => Ok(()), END_ID => Ok(()),
BYTE_ID => reader.skip_i8(), BYTE_ID => reader.skip_i8(),
@ -239,7 +239,10 @@ impl NbtTag {
} }
} }
pub fn deserialize_data<R: NbtReadHelper>(reader: &mut R, tag_id: u8) -> Result<Self, Error> { pub fn deserialize_data<'a, R: NbtReadHelper<'a>>(
reader: &mut R,
tag_id: u8,
) -> Result<Self, Error> {
match tag_id { match tag_id {
END_ID => Ok(Self::End), END_ID => Ok(Self::End),
BYTE_ID => { BYTE_ID => {
@ -283,7 +286,7 @@ impl NbtTag {
} }
Ok(Self::ByteArray(byte_array.into())) Ok(Self::ByteArray(byte_array.into()))
} }
STRING_ID => Ok(Self::String(reader.get_string()?.into())), STRING_ID => Ok(Self::String(reader.get_string()?.into_owned().into())),
LIST_ID => { LIST_ID => {
let tag_type_id = reader.get_u8()?; let tag_type_id = reader.get_u8()?;
let len = reader.get_i32()?; let len = reader.get_i32()?;

View File

@ -1,19 +1,60 @@
use pumpkin_macros::packet; use pumpkin_macros::packet;
use std::borrow::Cow;
use uuid::Uuid; use uuid::Uuid;
use crate::serial::PacketRead; use crate::serial::{PacketRead, PacketReadSlice, read_str_slice};
#[derive(Debug, PacketRead)] #[derive(Debug)]
#[packet(77)] #[packet(77)]
pub struct SCommandRequest { pub struct SCommandRequest<'a> {
pub command: String, pub command: Cow<'a, str>,
pub command_type: Cow<'a, str>,
// Command Origin
pub command_type: String,
pub command_uuid: Uuid, pub command_uuid: Uuid,
pub request_id: String, pub request_id: Cow<'a, str>,
pub player_actor_unique_id: i64, pub player_actor_unique_id: i64,
pub is_internal_source: bool, pub is_internal_source: bool,
pub version: String, pub version: Cow<'a, str>,
}
impl<'a> PacketReadSlice<'a> for SCommandRequest<'a> {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, std::io::Error> {
let command = Cow::Borrowed(read_str_slice(buf)?);
let command_type = Cow::Borrowed(read_str_slice(buf)?);
let command_uuid = Uuid::read_slice(buf)?;
let request_id = Cow::Borrowed(read_str_slice(buf)?);
let player_actor_unique_id = i64::read_slice(buf)?;
let is_internal_source = bool::read_slice(buf)?;
let version = Cow::Borrowed(read_str_slice(buf)?);
Ok(Self {
command,
command_type,
command_uuid,
request_id,
player_actor_unique_id,
is_internal_source,
version,
})
}
}
impl PacketRead for SCommandRequest<'static> {
fn read<R: std::io::Read>(reader: &mut R) -> Result<Self, std::io::Error> {
let command = Cow::Owned(String::read(reader)?);
let command_type = Cow::Owned(String::read(reader)?);
let command_uuid = Uuid::read(reader)?;
let request_id = Cow::Owned(String::read(reader)?);
let player_actor_unique_id = i64::read(reader)?;
let is_internal_source = bool::read(reader)?;
let version = Cow::Owned(String::read(reader)?);
Ok(Self {
command,
command_type,
command_uuid,
request_id,
player_actor_unique_id,
is_internal_source,
version,
})
}
} }

View File

@ -1,17 +1,62 @@
use crate::codec::var_ulong::VarULong; use crate::codec::var_ulong::VarULong;
use crate::serial::{PacketRead, PacketWrite}; use crate::serial::{PacketRead, PacketReadSlice, PacketWrite, read_str_slice};
use pumpkin_macros::packet; use pumpkin_macros::packet;
use std::borrow::Cow;
use std::io::{Error, Write};
pub const EMOTE_FLAG_SERVER_SIDE: u8 = 1 << 0; pub const EMOTE_FLAG_SERVER_SIDE: u8 = 1 << 0;
pub const EMOTE_FLAG_MUTE_CHAT: u8 = 1 << 1; pub const EMOTE_FLAG_MUTE_CHAT: u8 = 1 << 1;
#[derive(PacketRead, PacketWrite, Debug)] #[derive(Debug)]
#[packet(138)] #[packet(138)]
pub struct SEmote { pub struct SEmote<'a> {
pub runtime_entity_id: VarULong, pub runtime_entity_id: VarULong,
pub emote_id: String, pub emote_id: Cow<'a, str>,
pub emote_length: u32, pub emote_length: u32,
pub xuid: String, pub xuid: Cow<'a, str>,
pub platform_id: String, pub platform_id: Cow<'a, str>,
pub flags: u8, pub flags: u8,
} }
impl<'a> PacketReadSlice<'a> for SEmote<'a> {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
let runtime_entity_id = VarULong::read_slice(buf)?;
let emote_id = Cow::Borrowed(read_str_slice(buf)?);
let emote_length = u32::read_slice(buf)?;
let xuid = Cow::Borrowed(read_str_slice(buf)?);
let platform_id = Cow::Borrowed(read_str_slice(buf)?);
let flags = u8::read_slice(buf)?;
Ok(Self {
runtime_entity_id,
emote_id,
emote_length,
xuid,
platform_id,
flags,
})
}
}
impl PacketRead for SEmote<'static> {
fn read<R: std::io::Read>(reader: &mut R) -> Result<Self, Error> {
Ok(Self {
runtime_entity_id: VarULong::read(reader)?,
emote_id: Cow::Owned(String::read(reader)?),
emote_length: u32::read(reader)?,
xuid: Cow::Owned(String::read(reader)?),
platform_id: Cow::Owned(String::read(reader)?),
flags: u8::read(reader)?,
})
}
}
impl PacketWrite for SEmote<'_> {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
self.runtime_entity_id.write(writer)?;
self.emote_id.as_ref().write(writer)?;
self.emote_length.write(writer)?;
self.xuid.as_ref().write(writer)?;
self.platform_id.as_ref().write(writer)?;
self.flags.write(writer)
}
}

View File

@ -1,18 +1,30 @@
use crate::codec::var_int::VarInt; use crate::codec::var_int::VarInt;
use crate::serial::PacketRead; use crate::serial::{PacketRead, PacketReadSlice, read_str_slice};
use pumpkin_macros::packet; use pumpkin_macros::packet;
use std::borrow::Cow;
use std::io::{Error, Read}; use std::io::{Error, Read};
#[derive(Debug)]
#[packet(101)] #[packet(101)]
pub struct SModalFormResponse { pub struct SModalFormResponse<'a> {
pub form_id: VarInt, pub form_id: VarInt,
pub form_data: Option<String>, pub form_data: Option<Cow<'a, str>>,
} }
impl PacketRead for SModalFormResponse { impl<'a> PacketReadSlice<'a> for SModalFormResponse<'a> {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> { fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
let form_id = VarInt::read(reader)?; let form_id = VarInt::read_slice(buf)?;
let form_data = Option::<String>::read(reader)?; let form_data = bool::read_slice(buf)?
.then(|| read_str_slice(buf).map(Cow::Borrowed))
.transpose()?;
Ok(Self { form_id, form_data })
}
}
impl PacketRead for SModalFormResponse<'static> {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
let form_id = VarInt::read(reader)?;
let form_data = Option::<String>::read(reader)?.map(Cow::Owned);
Ok(Self { form_id, form_data }) Ok(Self { form_id, form_data })
} }
} }

View File

@ -1,52 +1,70 @@
use crate::{ use crate::{
codec::var_uint::VarUInt, codec::var_uint::VarUInt,
serial::{PacketRead, PacketWrite}, serial::{PacketRead, PacketReadSlice, PacketWrite, read_str_slice},
}; };
use pumpkin_macros::packet; use pumpkin_macros::packet;
use std::borrow::Cow;
use std::io::{Error, ErrorKind, Read, Write}; use std::io::{Error, ErrorKind, Read, Write};
#[derive(Debug)] #[derive(Debug)]
#[packet(9)] #[packet(9)]
pub struct SText { pub struct SText<'a> {
pub needs_translation: bool, pub needs_translation: bool,
pub r#type: TextPacketType, pub r#type: TextPacketType,
pub source_name: String, pub source_name: Cow<'a, str>,
pub message: String, pub message: Cow<'a, str>,
pub parameters: Vec<String>, pub parameters: Vec<Cow<'a, str>>,
pub xuid: String, pub xuid: Cow<'a, str>,
pub platform_chat_id: String, pub platform_chat_id: Cow<'a, str>,
pub filtered_message: Option<String>, pub filtered_message: Option<Cow<'a, str>>,
} }
impl SText { impl SText<'_> {
const fn get_category(&self) -> u8 {
match self.r#type {
TextPacketType::Raw
| TextPacketType::Tip
| TextPacketType::System
| TextPacketType::JsonWhisper
| TextPacketType::JsonAnnouncement
| TextPacketType::Json => 0,
TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => 1,
TextPacketType::Translation | TextPacketType::Popup | TextPacketType::JukeboxPopup => 2,
}
}
}
impl SText<'static> {
#[must_use] #[must_use]
pub fn new(message: String, source_name: String) -> Self { pub fn new(message: String, source_name: String) -> Self {
Self { Self {
needs_translation: false, needs_translation: false,
r#type: TextPacketType::Chat, r#type: TextPacketType::Chat,
source_name, source_name: Cow::Owned(source_name),
message: if message.is_empty() { message: Cow::Owned(if message.is_empty() {
" ".to_string() " ".to_string()
} else { } else {
message message
}, }),
parameters: Vec::new(), parameters: Vec::new(),
xuid: String::new(), xuid: Cow::Borrowed(""),
platform_chat_id: String::new(), platform_chat_id: Cow::Borrowed(""),
filtered_message: None, filtered_message: None,
} }
} }
#[must_use] #[must_use]
pub const fn translation(message: String, parameters: Vec<String>) -> Self { pub fn translation(message: String, parameters: Vec<String>) -> Self {
Self { Self {
needs_translation: true, needs_translation: true,
r#type: TextPacketType::Translation, r#type: TextPacketType::Translation,
source_name: String::new(), source_name: Cow::Borrowed(""),
message, message: Cow::Owned(message),
parameters, parameters: parameters.into_iter().map(Cow::Owned).collect(),
xuid: String::new(), xuid: Cow::Borrowed(""),
platform_chat_id: String::new(), platform_chat_id: Cow::Borrowed(""),
filtered_message: None, filtered_message: None,
} }
} }
@ -56,15 +74,15 @@ impl SText {
Self { Self {
needs_translation: false, needs_translation: false,
r#type: TextPacketType::System, r#type: TextPacketType::System,
source_name: String::new(), source_name: Cow::Borrowed(""),
message: if message.is_empty() { message: Cow::Owned(if message.is_empty() {
" ".to_string() " ".to_string()
} else { } else {
message message
}, }),
parameters: Vec::new(), parameters: Vec::new(),
xuid: String::new(), xuid: Cow::Borrowed(""),
platform_chat_id: String::new(), platform_chat_id: Cow::Borrowed(""),
filtered_message: None, filtered_message: None,
} }
} }
@ -74,37 +92,72 @@ impl SText {
Self { Self {
needs_translation: false, needs_translation: false,
r#type: TextPacketType::Json, r#type: TextPacketType::Json,
source_name: String::new(), source_name: Cow::Borrowed(""),
message, message: Cow::Owned(message),
parameters: Vec::new(), parameters: Vec::new(),
xuid: String::new(), xuid: Cow::Borrowed(""),
platform_chat_id: String::new(), platform_chat_id: Cow::Borrowed(""),
filtered_message: None, filtered_message: None,
} }
} }
const fn get_category(&self) -> u8 {
match self.r#type {
TextPacketType::Raw
| TextPacketType::Tip
| TextPacketType::System
| TextPacketType::JsonWhisper
| TextPacketType::JsonAnnouncement
| TextPacketType::Json => 0, // CATEGORY_MESSAGE_ONLY
TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => 1, // CATEGORY_AUTHORED_MESSAGE
TextPacketType::Translation | TextPacketType::Popup | TextPacketType::JukeboxPopup => 2, // CATEGORY_MESSAGE_WITH_PARAMETERS
}
}
} }
impl PacketRead for SText { impl<'a> PacketReadSlice<'a> for SText<'a> {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
let needs_translation = bool::read_slice(buf)?;
let _category = u8::read_slice(buf)?;
let r#type = TextPacketType::read_slice(buf)?;
let mut source_name = "";
let message;
let mut parameters: Vec<Cow<'a, str>> = Vec::new();
match r#type {
TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => {
source_name = read_str_slice(buf)?;
message = read_str_slice(buf)?;
}
TextPacketType::Raw
| TextPacketType::Tip
| TextPacketType::System
| TextPacketType::JsonWhisper
| TextPacketType::Json
| TextPacketType::JsonAnnouncement => {
message = read_str_slice(buf)?;
}
TextPacketType::Translation | TextPacketType::Popup | TextPacketType::JukeboxPopup => {
message = read_str_slice(buf)?;
let count = VarUInt::read_slice(buf)?.0 as usize;
for _ in 0..count {
parameters.push(Cow::Borrowed(read_str_slice(buf)?));
}
}
}
let xuid = read_str_slice(buf)?;
let platform_chat_id = read_str_slice(buf)?;
let filtered_message = bool::read_slice(buf)?
.then(|| read_str_slice(buf).map(Cow::Borrowed))
.transpose()?;
Ok(Self {
needs_translation,
r#type,
source_name: Cow::Borrowed(source_name),
message: Cow::Borrowed(message),
parameters,
xuid: Cow::Borrowed(xuid),
platform_chat_id: Cow::Borrowed(platform_chat_id),
filtered_message,
})
}
}
impl PacketRead for SText<'static> {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> { fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
let needs_translation = bool::read(reader)?; let needs_translation = bool::read(reader)?;
let _category = u8::read(reader)?; let _category = u8::read(reader)?;
let r#type = TextPacketType::read(reader)?; let r#type = TextPacketType::read(reader)?;
let mut source_name = String::new(); let mut source_name = String::new();
@ -129,7 +182,7 @@ impl PacketRead for SText {
message = String::read(reader)?; message = String::read(reader)?;
let count = VarUInt::read(reader)?.0 as usize; let count = VarUInt::read(reader)?.0 as usize;
for _ in 0..count { for _ in 0..count {
parameters.push(String::read(reader)?); parameters.push(Cow::Owned(String::read(reader)?));
} }
} }
} }
@ -138,23 +191,23 @@ impl PacketRead for SText {
let platform_chat_id = String::read(reader)?; let platform_chat_id = String::read(reader)?;
let filtered_message = bool::read(reader)? let filtered_message = bool::read(reader)?
.then(|| String::read(reader)) .then(|| String::read(reader).map(Cow::Owned))
.transpose()?; .transpose()?;
Ok(Self { Ok(Self {
needs_translation, needs_translation,
r#type, r#type,
source_name, source_name: Cow::Owned(source_name),
message, message: Cow::Owned(message),
parameters, parameters,
xuid, xuid: Cow::Owned(xuid),
platform_chat_id, platform_chat_id: Cow::Owned(platform_chat_id),
filtered_message, filtered_message,
}) })
} }
} }
impl PacketWrite for SText { impl PacketWrite for SText<'_> {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> { fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
self.needs_translation.write(writer)?; self.needs_translation.write(writer)?;
@ -165,8 +218,8 @@ impl PacketWrite for SText {
match self.r#type { match self.r#type {
TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => { TextPacketType::Chat | TextPacketType::Whisper | TextPacketType::Announcement => {
self.source_name.write(writer)?; self.source_name.as_ref().write(writer)?;
self.message.write(writer)?; self.message.as_ref().write(writer)?;
} }
TextPacketType::Raw TextPacketType::Raw
| TextPacketType::Tip | TextPacketType::Tip
@ -174,23 +227,23 @@ impl PacketWrite for SText {
| TextPacketType::JsonWhisper | TextPacketType::JsonWhisper
| TextPacketType::Json | TextPacketType::Json
| TextPacketType::JsonAnnouncement => { | TextPacketType::JsonAnnouncement => {
self.message.write(writer)?; self.message.as_ref().write(writer)?;
} }
TextPacketType::Translation | TextPacketType::Popup | TextPacketType::JukeboxPopup => { TextPacketType::Translation | TextPacketType::Popup | TextPacketType::JukeboxPopup => {
self.message.write(writer)?; self.message.as_ref().write(writer)?;
VarUInt(self.parameters.len() as u32).write(writer)?; VarUInt(self.parameters.len() as u32).write(writer)?;
for param in &self.parameters { for param in &self.parameters {
param.write(writer)?; param.as_ref().write(writer)?;
} }
} }
} }
self.xuid.write(writer)?; self.xuid.as_ref().write(writer)?;
self.platform_chat_id.write(writer)?; self.platform_chat_id.as_ref().write(writer)?;
if let Some(msg) = &self.filtered_message { if let Some(msg) = &self.filtered_message {
true.write(writer)?; true.write(writer)?;
msg.write(writer)?; msg.as_ref().write(writer)?;
} else { } else {
false.write(writer)?; false.write(writer)?;
} }
@ -241,3 +294,23 @@ impl PacketRead for TextPacketType {
} }
} }
} }
impl<'a> PacketReadSlice<'a> for TextPacketType {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
match u8::read_slice(buf)? {
0 => Ok(Self::Raw),
1 => Ok(Self::Chat),
2 => Ok(Self::Translation),
3 => Ok(Self::Popup),
4 => Ok(Self::JukeboxPopup),
5 => Ok(Self::Tip),
6 => Ok(Self::System),
7 => Ok(Self::Whisper),
8 => Ok(Self::Announcement),
9 => Ok(Self::JsonWhisper),
10 => Ok(Self::Json),
11 => Ok(Self::JsonAnnouncement),
_ => Err(Error::new(ErrorKind::InvalidData, "Unknown Text Type")),
}
}
}

View File

@ -106,16 +106,30 @@ fn read_length_prefixed_component(
) -> Result<(DataComponent, Box<dyn DataComponentImpl>), ReadingError> { ) -> Result<(DataComponent, Box<dyn DataComponentImpl>), ReadingError> {
let id = read_component_id(read)?; let id = read_component_id(read)?;
let byte_len = read.get_var_int()?.0; let byte_len = read.get_var_int()?.0;
let byte_len = byte_len let byte_len: usize = byte_len
.try_into() .try_into()
.map_err(|_| ReadingError::Message("Negative component data length".into()))?; .map_err(|_| ReadingError::Message("Negative component data length".into()))?;
let component_data = read.read_boxed_slice(byte_len)?;
let component_impl = if id == DataComponent::CustomName { let component_impl = if byte_len <= 256 {
let mut stack_buf = [0u8; 256];
let slice = &mut stack_buf[..byte_len];
read.read_bytes_to_buf(slice)?;
if id == DataComponent::CustomName {
decode_custom_name(slice)?
} else {
let mut cursor = Cursor::new(slice);
deserialize(id, &mut cursor)?
}
} else {
let mut component_data = vec![0u8; byte_len];
read.read_bytes_to_buf(&mut component_data)?;
if id == DataComponent::CustomName {
decode_custom_name(component_data.as_ref())? decode_custom_name(component_data.as_ref())?
} else { } else {
let mut cursor = Cursor::new(component_data); let mut cursor = Cursor::new(component_data);
deserialize(id, &mut cursor)? deserialize(id, &mut cursor)?
}
}; };
Ok((id, component_impl)) Ok((id, component_impl))
@ -297,13 +311,11 @@ impl ItemComponentHash {
pub fn read(read: &mut impl NetworkReadExt) -> Result<Self, ReadingError> { pub fn read(read: &mut impl NetworkReadExt) -> Result<Self, ReadingError> {
const MAX_COMPONENTS: i32 = 256; const MAX_COMPONENTS: i32 = 256;
let mut added = Vec::new();
let mut removed = Vec::new();
let added_length = read.get_var_int()?; let added_length = read.get_var_int()?;
if added_length.0 < 0 || added_length.0 > MAX_COMPONENTS { if added_length.0 < 0 || added_length.0 > MAX_COMPONENTS {
return Err(ReadingError::Message("added_length out of bounds".into())); return Err(ReadingError::Message("added_length out of bounds".into()));
} }
let mut added = Vec::with_capacity(added_length.0 as usize);
for _ in 0..added_length.0 { for _ in 0..added_length.0 {
let component_id = read.get_var_int()?; let component_id = read.get_var_int()?;
let component_value = read.get_i32()?; let component_value = read.get_i32()?;
@ -314,6 +326,7 @@ impl ItemComponentHash {
if removed_length.0 < 0 || removed_length.0 > MAX_COMPONENTS { if removed_length.0 < 0 || removed_length.0 > MAX_COMPONENTS {
return Err(ReadingError::Message("removed_length out of bounds".into())); return Err(ReadingError::Message("removed_length out of bounds".into()));
} }
let mut removed = Vec::with_capacity(removed_length.0 as usize);
for _ in 0..removed_length.0 { for _ in 0..removed_length.0 {
let component_id = read.get_var_int()?; let component_id = read.get_var_int()?;
removed.push(component_id); removed.push(component_id);

View File

@ -16,12 +16,12 @@ use crate::{
/// initializes the player's world view, dimension settings, and local game /// initializes the player's world view, dimension settings, and local game
/// rules. Once received, the client begins rendering the world. /// rules. Once received, the client begins rendering the world.
#[java_packet(PLAY_LOGIN)] #[java_packet(PLAY_LOGIN)]
pub struct CLogin { pub struct CLogin<'a> {
/// The unique ID assigned to the player for the current session. /// The unique ID assigned to the player for the current session.
pub entity_id: i32, pub entity_id: i32,
pub is_hardcore: bool, pub is_hardcore: bool,
/// A list of all dimensions present on the server (e.g., overworld, nether, end). /// A list of all dimensions present on the server (e.g., overworld, nether, end).
pub dimension_names: Vec<ResourceLocation>, pub dimension_names: &'a [ResourceLocation],
pub max_players: VarInt, pub max_players: VarInt,
/// The number of chunks the client will render in each direction. /// The number of chunks the client will render in each direction.
pub view_distance: VarInt, pub view_distance: VarInt,
@ -38,14 +38,14 @@ pub struct CLogin {
pub enforce_secure_chat: bool, pub enforce_secure_chat: bool,
} }
impl CLogin { impl<'a> CLogin<'a> {
#[expect(clippy::too_many_arguments)] #[expect(clippy::too_many_arguments)]
#[expect(clippy::fn_params_excessive_bools)] #[expect(clippy::fn_params_excessive_bools)]
#[must_use] #[must_use]
pub const fn new( pub const fn new(
entity_id: i32, entity_id: i32,
is_hardcore: bool, is_hardcore: bool,
dimension_names: Vec<ResourceLocation>, dimension_names: &'a [ResourceLocation],
max_players: VarInt, max_players: VarInt,
view_distance: VarInt, view_distance: VarInt,
simulated_distance: VarInt, simulated_distance: VarInt,
@ -73,7 +73,7 @@ impl CLogin {
} }
} }
impl ClientPacket for CLogin { impl ClientPacket for CLogin<'_> {
fn write_packet_data( fn write_packet_data(
&self, &self,
mut write: impl std::io::Write, mut write: impl std::io::Write,
@ -81,7 +81,7 @@ impl ClientPacket for CLogin {
) -> Result<(), WritingError> { ) -> Result<(), WritingError> {
write.write_i32_be(self.entity_id)?; write.write_i32_be(self.entity_id)?;
write.write_bool(self.is_hardcore)?; write.write_bool(self.is_hardcore)?;
write.write_list(&self.dimension_names, |write, dim| write.write_string(dim))?; write.write_list(self.dimension_names, |write, dim| write.write_string(dim))?;
write.write_var_int(&self.max_players)?; write.write_var_int(&self.max_players)?;
write.write_var_int(&self.view_distance)?; write.write_var_int(&self.view_distance)?;
write.write_var_int(&self.simulated_distance)?; write.write_var_int(&self.simulated_distance)?;

View File

@ -84,9 +84,9 @@ impl ClientPacket for CPlayerPosition {
} }
} }
impl ServerPacket for CPlayerPosition { impl<'a> ServerPacket<'a> for CPlayerPosition {
fn read( fn read(
mut read: impl std::io::Read, read: &mut &'a [u8],
_version: &JavaMinecraftVersion, _version: &JavaMinecraftVersion,
) -> Result<Self, crate::ser::ReadingError> { ) -> Result<Self, crate::ser::ReadingError> {
Ok(Self { Ok(Self {

View File

@ -191,7 +191,10 @@ impl<R: AsyncRead + Unpin> TCPNetworkDecoder<R> {
} }
} }
let payload = self.payload_scratch.split().freeze(); let payload = self
.payload_scratch
.split_to(self.payload_scratch.len())
.freeze();
Ok(RawPacket { Ok(RawPacket {
id: packet_id, id: packet_id,
@ -456,4 +459,40 @@ mod tests {
assert_eq!(raw_packet.payload.as_ref(), payload); assert_eq!(raw_packet.payload.as_ref(), payload);
Ok(()) Ok(())
} }
/// Test decoding multiple packets sequentially to verify capacity is retained for zero allocation
#[tokio::test]
async fn decode_multiple_packets_zero_allocation() -> Result<(), Box<dyn std::error::Error>> {
let packet1 = build_packet(1, b"Hello", false, None, None)?;
let packet2 = build_packet(2, b"World", false, None, None)?;
let mut stream = Vec::new();
stream.extend_from_slice(&packet1);
stream.extend_from_slice(&packet2);
let mut decoder = TCPNetworkDecoder::new(stream.as_slice());
let p1 = decoder.get_raw_packet().await?;
assert_eq!(p1.id, 1);
assert_eq!(p1.payload.as_ref(), b"Hello");
drop(p1);
let cap_after_p1 = decoder.payload_scratch.capacity();
assert!(
cap_after_p1 > 0,
"Capacity should be allocated after first read"
);
let p2 = decoder.get_raw_packet().await?;
assert_eq!(p2.id, 2);
assert_eq!(p2.payload.as_ref(), b"World");
drop(p2);
let cap_after_p2 = decoder.payload_scratch.capacity();
assert_eq!(
cap_after_p2, cap_after_p1,
"Buffer capacity should be retained and reused without new heap allocations"
);
Ok(())
}
} }

View File

@ -204,9 +204,6 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
/// NOTE: This method does not flush. Call [`Self::flush`] to flush buffered data. /// NOTE: This method does not flush. Call [`Self::flush`] to flush buffered data.
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub async fn write_packet(&mut self, packet_data: Bytes) -> Result<(), PacketEncodeError> { pub async fn write_packet(&mut self, packet_data: Bytes) -> Result<(), PacketEncodeError> {
// We need to know the length of the compressed buffer and serde is not async :(
// We need to write to a buffer here 😔
let data_len = packet_data.len(); let data_len = packet_data.len();
if data_len > MAX_PACKET_DATA_SIZE { if data_len > MAX_PACKET_DATA_SIZE {
return Err(PacketEncodeError::TooLong(data_len)); return Err(PacketEncodeError::TooLong(data_len));
@ -218,14 +215,13 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
)) ))
})?; })?;
if let Some((compression_threshold, compression_level)) = self.compression { let mut header_buf = [0u8; 10];
if data_len >= compression_threshold { let mut header_cursor = std::io::Cursor::new(&mut header_buf[..]);
// Pushed before data:
// Length of (Data Length) + length of compressed (Packet ID + Data)
// Length of uncompressed (Packet ID + Data)
// TODO: We need the compressed length at the beginning of the packet so we need to write to let payload_to_write: &[u8] = if let Some((compression_threshold, compression_level)) =
// buf here :( Is there a magic way to find a compressed length? self.compression
{
if data_len >= compression_threshold {
self.compress_packet_data(packet_data.as_ref(), compression_level)?; self.compress_packet_data(packet_data.as_ref(), compression_level)?;
debug_assert!(!self.compression_scratch.is_empty()); debug_assert!(!self.compression_scratch.is_empty());
@ -245,24 +241,14 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
} }
full_packet_len_var_int full_packet_len_var_int
.encode_async(self.writer.as_mut().unwrap()) .encode(&mut header_cursor)
.await
.map_err(|err| PacketEncodeError::Message(err.to_string()))?; .map_err(|err| PacketEncodeError::Message(err.to_string()))?;
data_len_var_int data_len_var_int
.encode_async(self.writer.as_mut().unwrap()) .encode(&mut header_cursor)
.await
.map_err(|err| PacketEncodeError::Message(err.to_string()))?; .map_err(|err| PacketEncodeError::Message(err.to_string()))?;
self.writer
.as_mut()
.unwrap()
.write_all(&self.compression_scratch)
.await
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
} else {
// Pushed before data:
// Length of (Data Length) + length of compressed (Packet ID + Data)
// 0 to indicate uncompressed
self.compression_scratch.as_slice()
} else {
let data_len_var_int: VarInt = 0.into(); let data_len_var_int: VarInt = 0.into();
let full_packet_len_var_int: VarInt = (data_len_var_int.written_size() + data_len) let full_packet_len_var_int: VarInt = (data_len_var_int.written_size() + data_len)
.try_into() .try_into()
@ -279,24 +265,15 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
} }
full_packet_len_var_int full_packet_len_var_int
.encode_async(self.writer.as_mut().unwrap()) .encode(&mut header_cursor)
.await
.map_err(|err| PacketEncodeError::Message(err.to_string()))?; .map_err(|err| PacketEncodeError::Message(err.to_string()))?;
data_len_var_int data_len_var_int
.encode_async(self.writer.as_mut().unwrap()) .encode(&mut header_cursor)
.await
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
self.writer
.as_mut()
.unwrap()
.write_all(&packet_data)
.await
.map_err(|err| PacketEncodeError::Message(err.to_string()))?; .map_err(|err| PacketEncodeError::Message(err.to_string()))?;
packet_data.as_ref()
} }
} else { } else {
// Pushed before data:
// Length of Packet ID + Data
let full_packet_len_var_int: VarInt = data_len_var_int; let full_packet_len_var_int: VarInt = data_len_var_int;
let complete_serialization_length = let complete_serialization_length =
@ -306,16 +283,27 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
} }
full_packet_len_var_int full_packet_len_var_int
.encode_async(self.writer.as_mut().unwrap()) .encode(&mut header_cursor)
.await
.map_err(|err| PacketEncodeError::Message(err.to_string()))?; .map_err(|err| PacketEncodeError::Message(err.to_string()))?;
self.writer
packet_data.as_ref()
};
let header_len = header_cursor.position() as usize;
let header_bytes = &header_buf[..header_len];
let writer = self
.writer
.as_mut() .as_mut()
.unwrap() .ok_or_else(|| PacketEncodeError::Message("Writer missing".into()))?;
.write_all(&packet_data)
writer
.write_all(header_bytes)
.await
.map_err(|err| PacketEncodeError::Message(err.to_string()))?;
writer
.write_all(payload_to_write)
.await .await
.map_err(|err| PacketEncodeError::Message(err.to_string()))?; .map_err(|err| PacketEncodeError::Message(err.to_string()))?;
}
Ok(()) Ok(())
} }
@ -323,7 +311,7 @@ impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
pub async fn flush(&mut self) -> Result<(), PacketEncodeError> { pub async fn flush(&mut self) -> Result<(), PacketEncodeError> {
self.writer self.writer
.as_mut() .as_mut()
.unwrap() .ok_or_else(|| PacketEncodeError::Message("Writer missing".into()))?
.flush() .flush()
.await .await
.map_err(|err| PacketEncodeError::Message(err.to_string())) .map_err(|err| PacketEncodeError::Message(err.to_string()))

View File

@ -3,15 +3,17 @@ use pumpkin_macros::java_packet;
use crate::{ServerPacket, ser::ReadingError}; use crate::{ServerPacket, ser::ReadingError};
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
/// This packet signals to the server that the client is ready to transition /// This packet signals to the server that the client is ready to transition
/// from the `Configuration` state to the `Play` state. /// from the `Configuration` state to the `Play` state.
#[java_packet(CONFIG_FINISH_CONFIGURATION)] #[java_packet(CONFIG_FINISH_CONFIGURATION)]
pub struct SAcknowledgeFinishConfig; pub struct SAcknowledgeFinishConfig;
impl ServerPacket for SAcknowledgeFinishConfig { impl<'a> ServerPacket<'a> for SAcknowledgeFinishConfig {
fn read(_bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(
_bytebuf: &mut &'a [u8],
_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> {
Ok(Self) Ok(Self)
} }
} }

View File

@ -5,16 +5,15 @@ use crate::VarInt;
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
/// Sent by the client to inform the server about its local settings /// Sent by the client to inform the server about its local settings
#[java_packet(CONFIG_CLIENT_INFORMATION)] #[java_packet(CONFIG_CLIENT_INFORMATION)]
pub struct SClientInformationConfig { pub struct SClientInformationConfig<'a> {
/// The language code used by the client (e.g., "`en_us`") /// The language code used by the client (e.g., "`en_us`")
pub locale: String, pub locale: &'a str,
/// The maximum number of chunks the client renders /// The maximum number of chunks the client renders
pub view_distance: i8, pub view_distance: i8,
/// Visibility of chat messages (0: Enabled, 1: Commands Only, 2: Hidden) /// Visibility of chat messages (0: Enabled, 1: Commands Only, 2: Hidden)
@ -31,10 +30,10 @@ pub struct SClientInformationConfig {
pub server_listing: bool, pub server_listing: bool,
} }
impl ServerPacket for SClientInformationConfig { impl<'a> ServerPacket<'a> for SClientInformationConfig<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
locale: bytebuf.get_str()?.into_string(), locale: bytebuf.get_str_borrowed()?,
view_distance: bytebuf.get_i8()?, view_distance: bytebuf.get_i8()?,
chat_mode: bytebuf.get_var_int()?, chat_mode: bytebuf.get_var_int()?,
chat_colors: bytebuf.get_bool()?, chat_colors: bytebuf.get_bool()?,

View File

@ -1,10 +1,11 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::CONFIG_COOKIE_RESPONSE; use pumpkin_data::packet::serverbound::CONFIG_COOKIE_RESPONSE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use crate::{ReadingError, ServerPacket, ser::NetworkReadExt}; use crate::{
ReadingError, ServerPacket,
ser::{NetworkReadExt, NetworkReadSliceExt},
};
/// The maximum allowed size for a cookie payload (5 KiB). /// The maximum allowed size for a cookie payload (5 KiB).
const MAX_COOKIE_LENGTH: usize = 5120; const MAX_COOKIE_LENGTH: usize = 5120;
@ -14,19 +15,18 @@ const MAX_COOKIE_LENGTH: usize = 5120;
/// Cookies allow servers to store small amounts of data on the client side, /// Cookies allow servers to store small amounts of data on the client side,
/// which can be retrieved later (e.g., for session tracking or preferences) /// which can be retrieved later (e.g., for session tracking or preferences)
#[java_packet(CONFIG_COOKIE_RESPONSE)] #[java_packet(CONFIG_COOKIE_RESPONSE)]
pub struct SConfigCookieResponse { pub struct SConfigCookieResponse<'a> {
/// The unique identifier for the cookie being returned /// The unique identifier for the cookie being returned
pub key: Box<str>, pub key: &'a str,
/// Indicates whether a payload is attached to this response /// Indicates whether a payload is attached to this response
pub has_payload: bool, pub has_payload: bool,
/// The actual data stored in the cookie. Limited to 5120 bytes /// The actual data stored in the cookie. Limited to 5120 bytes
pub payload: Option<Box<[u8]>>, pub payload: Option<&'a [u8]>,
} }
impl ServerPacket for SConfigCookieResponse { impl<'a> ServerPacket<'a> for SConfigCookieResponse<'a> {
fn read(read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
let mut read = read; let key = read.get_str_borrowed()?;
let key = read.get_str()?;
let has_payload = read.get_bool()?; let has_payload = read.get_bool()?;
if !has_payload { if !has_payload {
@ -42,7 +42,7 @@ impl ServerPacket for SConfigCookieResponse {
return Err(ReadingError::TooLarge("SConfigCookieResponse".to_string())); return Err(ReadingError::TooLarge("SConfigCookieResponse".to_string()));
} }
let payload = read.read_boxed_slice(payload_length)?; let payload = read.read_slice_borrowed(payload_length)?;
Ok(Self { Ok(Self {
key, key,
has_payload, has_payload,

View File

@ -1,25 +1,22 @@
use pumpkin_data::packet::serverbound::CONFIG_CUSTOM_CLICK_ACTION;
use pumpkin_macros::java_packet;
use pumpkin_util::resource_location::ResourceLocation;
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_data::packet::serverbound::CONFIG_CUSTOM_CLICK_ACTION;
use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(CONFIG_CUSTOM_CLICK_ACTION)] #[java_packet(CONFIG_CUSTOM_CLICK_ACTION)]
pub struct SCustomClickAction { pub struct SCustomClickAction<'a> {
pub action_id: ResourceLocation, pub action_id: &'a str,
pub payload: Option<Box<[u8]>>, pub payload: Option<&'a [u8]>,
} }
impl ServerPacket for SCustomClickAction { impl<'a> ServerPacket<'a> for SCustomClickAction<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
action_id: bytebuf.get_str()?.into_string(), action_id: bytebuf.get_str_borrowed()?,
payload: bytebuf.get_option(|v| v.read_remaining_to_boxed_slice(32767))?, payload: bytebuf.get_option(|v| v.read_remaining_slice_borrowed(32767))?,
}) })
} }
} }

View File

@ -6,15 +6,14 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(CONFIG_KEEP_ALIVE)] #[java_packet(CONFIG_KEEP_ALIVE)]
pub struct SKeepAlive { pub struct SKeepAlive {
pub keep_alive_id: i64, pub keep_alive_id: i64,
} }
impl ServerPacket for SKeepAlive { impl<'a> ServerPacket<'a> for SKeepAlive {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
keep_alive_id: bytebuf.get_i64_be()?, keep_alive_id: bytebuf.get_i64_be()?,
}) })

View File

@ -8,7 +8,6 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(CONFIG_SELECT_KNOWN_PACKS)] #[java_packet(CONFIG_SELECT_KNOWN_PACKS)]
pub struct SKnownPacks { pub struct SKnownPacks {
@ -16,8 +15,8 @@ pub struct SKnownPacks {
// known_packs: &'a [KnownPack] // known_packs: &'a [KnownPack]
} }
impl ServerPacket for SKnownPacks { impl<'a> ServerPacket<'a> for SKnownPacks {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
known_pack_count: bytebuf.get_var_int()?, known_pack_count: bytebuf.get_var_int()?,
}) })

View File

@ -1,10 +1,8 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::CONFIG_CUSTOM_PAYLOAD; use pumpkin_data::packet::serverbound::CONFIG_CUSTOM_PAYLOAD;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use crate::{ReadingError, ServerPacket, ser::NetworkReadExt}; use crate::{ReadingError, ServerPacket, ser::NetworkReadSliceExt};
/// The maximum allowed size for a plugin message payload (1 MiB). /// The maximum allowed size for a plugin message payload (1 MiB).
const MAX_PAYLOAD_SIZE: usize = 1_048_576; const MAX_PAYLOAD_SIZE: usize = 1_048_576;
@ -14,19 +12,19 @@ const MAX_PAYLOAD_SIZE: usize = 1_048_576;
/// This allows mods, plugins, or proxy /// This allows mods, plugins, or proxy
/// software to send proprietary data over the standard Minecraft protocol. /// software to send proprietary data over the standard Minecraft protocol.
#[java_packet(CONFIG_CUSTOM_PAYLOAD)] #[java_packet(CONFIG_CUSTOM_PAYLOAD)]
pub struct SPluginMessage { pub struct SPluginMessage<'a> {
/// The name of the channel used to distinguish different types of messages. /// The name of the channel used to distinguish different types of messages.
/// Example: `minecraft:brand` or `velocity:main`. /// Example: `minecraft:brand` or `velocity:main`.
pub channel: Box<str>, pub channel: &'a str,
/// The payload sent by the client. /// The payload sent by the client.
pub data: Box<[u8]>, pub data: &'a [u8],
} }
impl ServerPacket for SPluginMessage { impl<'a> ServerPacket<'a> for SPluginMessage<'a> {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
channel: read.get_str()?, channel: read.get_str_borrowed()?,
data: read.read_remaining_to_boxed_slice(MAX_PAYLOAD_SIZE)?, data: read.read_remaining_slice_borrowed(MAX_PAYLOAD_SIZE)?,
}) })
} }
} }

View File

@ -6,7 +6,6 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -34,8 +33,8 @@ pub struct SConfigResourcePack {
pub result: VarInt, pub result: VarInt,
} }
impl ServerPacket for SConfigResourcePack { impl<'a> ServerPacket<'a> for SConfigResourcePack {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
uuid: bytebuf.get_uuid()?, uuid: bytebuf.get_uuid()?,
result: bytebuf.get_var_int()?, result: bytebuf.get_var_int()?,

View File

@ -1,5 +1,3 @@
use std::io::Read;
use crate::{ use crate::{
ClientPacket, ConnectionState, ReadingError, ServerPacket, VarInt, ser::NetworkReadExt, ClientPacket, ConnectionState, ReadingError, ServerPacket, VarInt, ser::NetworkReadExt,
ser::NetworkWriteExt, ser::NetworkWriteExt,
@ -24,8 +22,8 @@ pub struct SHandShake {
pub next_state: ConnectionState, pub next_state: ConnectionState,
} }
impl ServerPacket for SHandShake { impl<'a> ServerPacket<'a> for SHandShake {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
protocol_version: read.get_var_int()?, protocol_version: read.get_var_int()?,
server_address: read.get_str_bounded(255)?, server_address: read.get_str_bounded(255)?,

View File

@ -1,26 +1,25 @@
use pumpkin_data::packet::serverbound::LOGIN_COOKIE_RESPONSE; use pumpkin_data::packet::serverbound::LOGIN_COOKIE_RESPONSE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
#[java_packet(LOGIN_COOKIE_RESPONSE)] #[java_packet(LOGIN_COOKIE_RESPONSE)]
/// Response to a `CCookieRequest` (login) from the server. /// Response to a `CCookieRequest` (login) from the server.
/// The Notchian server only accepts responses of up to 5 kiB in size. /// The Notchian server only accepts responses of up to 5 kiB in size.
pub struct SLoginCookieResponse { pub struct SLoginCookieResponse<'a> {
pub key: Box<str>, pub key: &'a str,
pub payload: Option<Box<[u8]>>, // 5120, pub payload: Option<&'a [u8]>, // 5120,
} }
const MAX_COOKIE_LENGTH: usize = 5120; const MAX_COOKIE_LENGTH: usize = 5120;
impl ServerPacket for SLoginCookieResponse { impl<'a> ServerPacket<'a> for SLoginCookieResponse<'a> {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
let key = read.get_str()?; let key = read.get_str_borrowed()?;
let has_payload = read.get_bool()?; let has_payload = read.get_bool()?;
if !has_payload { if !has_payload {
@ -33,7 +32,7 @@ impl ServerPacket for SLoginCookieResponse {
return Err(ReadingError::TooLarge("SLoginCookieResponse".to_string())); return Err(ReadingError::TooLarge("SLoginCookieResponse".to_string()));
} }
let payload = read.read_boxed_slice(length)?; let payload = read.read_slice_borrowed(length)?;
Ok(Self { Ok(Self {
key, key,

View File

@ -1,26 +1,36 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::LOGIN_KEY; use pumpkin_data::packet::serverbound::LOGIN_KEY;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use crate::{ReadingError, ServerPacket, ser::NetworkReadExt}; use crate::{ReadingError, ServerPacket, ser::NetworkReadExt};
#[derive(Clone, Debug, PartialEq, Eq)]
#[java_packet(LOGIN_KEY)] #[java_packet(LOGIN_KEY)]
pub struct SEncryptionResponse { pub struct SEncryptionResponse {
pub shared_secret: Box<[u8]>, pub shared_secret: Box<[u8]>,
pub verify_token: Box<[u8]>, pub verify_token: Box<[u8]>,
} }
impl ServerPacket for SEncryptionResponse { impl<'a> ServerPacket<'a> for SEncryptionResponse {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(
let shared_secret_length = read.get_var_int()?.0 as usize; mut read: &mut &'a [u8],
let shared_secret = read.read_boxed_slice(shared_secret_length)?; _version: &JavaMinecraftVersion,
let verify_token_length = read.get_var_int()?.0 as usize; ) -> Result<Self, ReadingError> {
let verify_token = read.read_boxed_slice(verify_token_length)?; let shared_secret = read_encryption_buffer(&mut read)?;
let verify_token = read_encryption_buffer(&mut read)?;
Ok(Self { Ok(Self {
shared_secret, shared_secret,
verify_token, verify_token,
}) })
} }
} }
fn read_encryption_buffer(read: &mut impl NetworkReadExt) -> Result<Box<[u8]>, ReadingError> {
let length = read.get_var_int()?.0 as usize;
if length > 256 {
return Err(ReadingError::Message("Encryption payload too large".into()));
}
let mut data = vec![0u8; length];
read.read_bytes_to_buf(&mut data)?;
Ok(data.into_boxed_slice())
}

View File

@ -3,14 +3,16 @@ use pumpkin_macros::java_packet;
use crate::{ServerPacket, ser::ReadingError}; use crate::{ServerPacket, ser::ReadingError};
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
/// Acknowledgement to the `CLoginSuccess` packet sent by the server. /// Acknowledgement to the `CLoginSuccess` packet sent by the server.
#[java_packet(LOGIN_LOGIN_ACKNOWLEDGED)] #[java_packet(LOGIN_LOGIN_ACKNOWLEDGED)]
pub struct SLoginAcknowledged; pub struct SLoginAcknowledged;
impl ServerPacket for SLoginAcknowledged { impl<'a> ServerPacket<'a> for SLoginAcknowledged {
fn read(_bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(
_bytebuf: &mut &'a [u8],
_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> {
Ok(Self) Ok(Self)
} }
} }

View File

@ -1,5 +1,3 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::LOGIN_HELLO; use pumpkin_data::packet::serverbound::LOGIN_HELLO;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
@ -15,8 +13,8 @@ pub struct SLoginStart {
pub uuid: uuid::Uuid, pub uuid: uuid::Uuid,
} }
impl ServerPacket for SLoginStart { impl<'a> ServerPacket<'a> for SLoginStart {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
name: read.get_str_bounded(16)?, name: read.get_str_bounded(16)?,
uuid: read.get_uuid()?, uuid: read.get_uuid()?,

View File

@ -1,5 +1,3 @@
use std::io::Read;
use crate::{ReadingError, ServerPacket, VarInt, ser::NetworkReadExt}; use crate::{ReadingError, ServerPacket, VarInt, ser::NetworkReadExt};
use pumpkin_data::packet::serverbound::LOGIN_CUSTOM_QUERY_ANSWER; use pumpkin_data::packet::serverbound::LOGIN_CUSTOM_QUERY_ANSWER;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
@ -13,11 +11,11 @@ pub struct SLoginPluginResponse {
pub data: Option<Box<[u8]>>, pub data: Option<Box<[u8]>>,
} }
impl ServerPacket for SLoginPluginResponse { impl<'a> ServerPacket<'a> for SLoginPluginResponse {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
message_id: read.get_var_int()?, message_id: read.get_var_int()?,
data: read.get_option(|v| v.read_remaining_to_boxed_slice(MAX_PAYLOAD_SIZE))?, data: read.get_option(|v| crate::ser::read_remaining_bytes(v, MAX_PAYLOAD_SIZE))?,
}) })
} }
} }

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_ATTACK; use pumpkin_data::packet::serverbound::PLAY_ATTACK;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::codec::var_int::VarInt; use crate::codec::var_int::VarInt;
@ -14,9 +13,9 @@ pub struct SAttack {
pub entity_id: VarInt, pub entity_id: VarInt,
} }
impl ServerPacket for SAttack { impl<'a> ServerPacket<'a> for SAttack {
fn read( fn read(
mut bytebuf: impl Read, bytebuf: &mut &'a [u8],
_protocol_version: &JavaMinecraftVersion, _protocol_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {

View File

@ -8,7 +8,6 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_BUNDLE_ITEM_SELECTED)] #[java_packet(PLAY_BUNDLE_ITEM_SELECTED)]
pub struct SBundleItemSelected { pub struct SBundleItemSelected {
@ -16,8 +15,8 @@ pub struct SBundleItemSelected {
pub selected_item_index: VarInt, pub selected_item_index: VarInt,
} }
impl ServerPacket for SBundleItemSelected { impl<'a> ServerPacket<'a> for SBundleItemSelected {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
slot_id: bytebuf.get_var_int()?, slot_id: bytebuf.get_var_int()?,
selected_item_index: bytebuf.get_var_int()?, selected_item_index: bytebuf.get_var_int()?,

View File

@ -6,15 +6,14 @@ use pumpkin_data::packet::serverbound::PLAY_CHANGE_GAME_MODE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::GameMode; use pumpkin_util::GameMode;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_CHANGE_GAME_MODE)] #[java_packet(PLAY_CHANGE_GAME_MODE)]
pub struct SChangeGameMode { pub struct SChangeGameMode {
pub game_mode: GameMode, pub game_mode: GameMode,
} }
impl ServerPacket for SChangeGameMode { impl<'a> ServerPacket<'a> for SChangeGameMode {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
game_mode: GameMode::try_from(bytebuf.get_u8()? as i8) game_mode: GameMode::try_from(bytebuf.get_u8()? as i8)
.map_err(|()| crate::ser::ReadingError::Message("Invalid game mode".into()))?, .map_err(|()| crate::ser::ReadingError::Message("Invalid game mode".into()))?,

View File

@ -3,20 +3,19 @@ use pumpkin_macros::java_packet;
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_CHAT_COMMAND)] #[java_packet(PLAY_CHAT_COMMAND)]
pub struct SChatCommand { pub struct SChatCommand<'a> {
pub command: String, pub command: &'a str,
} }
impl ServerPacket for SChatCommand { impl<'a> ServerPacket<'a> for SChatCommand<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
command: bytebuf.get_str()?.into_string(), command: bytebuf.get_str_borrowed()?,
}) })
} }
} }

View File

@ -1,5 +1,3 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::PLAY_CHAT; use pumpkin_data::packet::serverbound::PLAY_CHAT;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
@ -8,46 +6,46 @@ use crate::{
ClientPacket, ServerPacket, ClientPacket, ServerPacket,
codec::var_int::VarInt, codec::var_int::VarInt,
ser::NetworkWriteExt, ser::NetworkWriteExt,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
#[java_packet(PLAY_CHAT)] #[java_packet(PLAY_CHAT)]
pub struct SChatMessage { pub struct SChatMessage<'a> {
pub message: Box<str>, pub message: &'a str,
pub timestamp: i64, pub timestamp: i64,
pub salt: i64, pub salt: i64,
pub signature: Option<Box<[u8]>>, pub signature: Option<&'a [u8]>,
pub message_count: VarInt, pub message_count: VarInt,
pub acknowledged: Box<[u8]>, // Bitset fixed 20 bits pub acknowledged: &'a [u8], // Bitset fixed 20 bits
pub checksum: u8, // 1.21.5 "fingerprint" checksum pub checksum: u8, // 1.21.5 "fingerprint" checksum
} }
impl ServerPacket for SChatMessage { impl<'a> ServerPacket<'a> for SChatMessage<'a> {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
message: read.get_str_bounded(256)?, message: read.get_str_bounded_borrowed(256)?,
timestamp: read.get_i64_be()?, timestamp: read.get_i64_be()?,
salt: read.get_i64_be()?, salt: read.get_i64_be()?,
signature: read.get_option(|v| v.read_boxed_slice(256))?, signature: read.get_option(|v| v.read_slice_borrowed(256))?,
message_count: read.get_var_int()?, message_count: read.get_var_int()?,
acknowledged: read.get_fixed_bitset(20)?, acknowledged: read.read_slice_borrowed(3)?,
checksum: read.get_u8()?, checksum: read.get_u8()?,
}) })
} }
} }
impl ClientPacket for SChatMessage { impl ClientPacket for SChatMessage<'_> {
fn write_packet_data( fn write_packet_data(
&self, &self,
mut write: impl std::io::Write, mut write: impl std::io::Write,
_version: &JavaMinecraftVersion, _version: &JavaMinecraftVersion,
) -> Result<(), crate::ser::WritingError> { ) -> Result<(), crate::ser::WritingError> {
write.write_string(&self.message)?; write.write_string(self.message)?;
write.write_i64_be(self.timestamp)?; write.write_i64_be(self.timestamp)?;
write.write_i64_be(self.salt)?; write.write_i64_be(self.salt)?;
write.write_option(&self.signature, |p, v| p.write_slice(v))?; write.write_option(&self.signature, |p, v| p.write_slice(v))?;
write.write_var_int(&self.message_count)?; write.write_var_int(&self.message_count)?;
write.write_fixed_bitset(20, self.acknowledged.clone())?; write.write_slice(self.acknowledged)?;
write.write_u8(self.checksum)?; write.write_u8(self.checksum)?;
Ok(()) Ok(())

View File

@ -5,15 +5,14 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_CHUNK_BATCH_RECEIVED; use pumpkin_data::packet::serverbound::PLAY_CHUNK_BATCH_RECEIVED;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_CHUNK_BATCH_RECEIVED)] #[java_packet(PLAY_CHUNK_BATCH_RECEIVED)]
pub struct SChunkBatch { pub struct SChunkBatch {
pub chunks_per_tick: f32, pub chunks_per_tick: f32,
} }
impl ServerPacket for SChunkBatch { impl<'a> ServerPacket<'a> for SChunkBatch {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
chunks_per_tick: bytebuf.get_f32_be()?, chunks_per_tick: bytebuf.get_f32_be()?,
}) })

View File

@ -22,8 +22,11 @@ pub struct SClickSlot {
pub carried_item: OptionalItemStackHash, pub carried_item: OptionalItemStackHash,
} }
impl ServerPacket for SClickSlot { impl<'a> ServerPacket<'a> for SClickSlot {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(
mut bytebuf: &mut &'a [u8],
_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> {
let sync_id = bytebuf.get_var_int()?; let sync_id = bytebuf.get_var_int()?;
let revision = bytebuf.get_var_int()?; let revision = bytebuf.get_var_int()?;
let slot = bytebuf.get_i16_be()?; let slot = bytebuf.get_i16_be()?;
@ -31,6 +34,11 @@ impl ServerPacket for SClickSlot {
let mode = SlotActionType::read(&mut bytebuf)?; let mode = SlotActionType::read(&mut bytebuf)?;
let length_of_array = bytebuf.get_var_int()?; let length_of_array = bytebuf.get_var_int()?;
if length_of_array.0 < 0 || length_of_array.0 > 256 {
return Err(ReadingError::Message(
"Changed slots length out of bounds".into(),
));
}
let mut array_of_changed_slots = Vec::with_capacity(length_of_array.0 as usize); let mut array_of_changed_slots = Vec::with_capacity(length_of_array.0 as usize);
for _ in 0..length_of_array.0 { for _ in 0..length_of_array.0 {
array_of_changed_slots.push(( array_of_changed_slots.push((

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_CLIENT_COMMAND; use pumpkin_data::packet::serverbound::PLAY_CLIENT_COMMAND;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -14,8 +13,8 @@ pub struct SClientCommand {
pub action_id: VarInt, pub action_id: VarInt,
} }
impl ServerPacket for SClientCommand { impl<'a> ServerPacket<'a> for SClientCommand {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
action_id: bytebuf.get_var_int()?, action_id: bytebuf.get_var_int()?,
}) })

View File

@ -1,17 +1,16 @@
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_data::packet::serverbound::PLAY_CLIENT_INFORMATION; use pumpkin_data::packet::serverbound::PLAY_CLIENT_INFORMATION;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
#[java_packet(PLAY_CLIENT_INFORMATION)] #[java_packet(PLAY_CLIENT_INFORMATION)]
pub struct SClientInformationPlay { pub struct SClientInformationPlay<'a> {
pub locale: String, // 16 pub locale: &'a str, // 16
pub view_distance: i8, pub view_distance: i8,
pub chat_mode: VarInt, // VarInt pub chat_mode: VarInt, // VarInt
pub chat_colors: bool, pub chat_colors: bool,
@ -21,10 +20,10 @@ pub struct SClientInformationPlay {
pub server_listing: bool, pub server_listing: bool,
} }
impl ServerPacket for SClientInformationPlay { impl<'a> ServerPacket<'a> for SClientInformationPlay<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
locale: bytebuf.get_str()?.to_string(), locale: bytebuf.get_str_borrowed()?,
view_distance: bytebuf.get_i8()?, view_distance: bytebuf.get_i8()?,
chat_mode: bytebuf.get_var_int()?, chat_mode: bytebuf.get_var_int()?,
chat_colors: bytebuf.get_bool()?, chat_colors: bytebuf.get_bool()?,

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_CONTAINER_CLOSE; use pumpkin_data::packet::serverbound::PLAY_CONTAINER_CLOSE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -14,8 +13,8 @@ pub struct SCloseContainer {
pub window_id: VarInt, pub window_id: VarInt,
} }
impl ServerPacket for SCloseContainer { impl<'a> ServerPacket<'a> for SCloseContainer {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
window_id: bytebuf.get_var_int()?, window_id: bytebuf.get_var_int()?,
}) })

View File

@ -5,22 +5,21 @@ use crate::VarInt;
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_COMMAND_SUGGESTION)] #[java_packet(PLAY_COMMAND_SUGGESTION)]
pub struct SCommandSuggestion { pub struct SCommandSuggestion<'a> {
pub id: VarInt, pub id: VarInt,
pub command: String, pub command: &'a str,
} }
impl ServerPacket for SCommandSuggestion { impl<'a> ServerPacket<'a> for SCommandSuggestion<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
id: bytebuf.get_var_int()?, id: bytebuf.get_var_int()?,
command: bytebuf.get_str()?.into_string(), command: bytebuf.get_str_borrowed()?,
}) })
} }
} }

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_ACCEPT_TELEPORTATION; use pumpkin_data::packet::serverbound::PLAY_ACCEPT_TELEPORTATION;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -14,8 +13,8 @@ pub struct SConfirmTeleport {
pub teleport_id: VarInt, pub teleport_id: VarInt,
} }
impl ServerPacket for SConfirmTeleport { impl<'a> ServerPacket<'a> for SConfirmTeleport {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
teleport_id: bytebuf.get_var_int()?, teleport_id: bytebuf.get_var_int()?,
}) })

View File

@ -7,7 +7,6 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[derive(Debug)] #[derive(Debug)]
#[java_packet(PLAY_CONTAINER_BUTTON_CLICK)] #[java_packet(PLAY_CONTAINER_BUTTON_CLICK)]
@ -16,8 +15,8 @@ pub struct SContainerButtonClick {
pub button_id: VarInt, pub button_id: VarInt,
} }
impl ServerPacket for SContainerButtonClick { impl<'a> ServerPacket<'a> for SContainerButtonClick {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
window_id: bytebuf.get_var_int()?, window_id: bytebuf.get_var_int()?,
button_id: bytebuf.get_var_int()?, button_id: bytebuf.get_var_int()?,

View File

@ -1,27 +1,25 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::PLAY_COOKIE_RESPONSE; use pumpkin_data::packet::serverbound::PLAY_COOKIE_RESPONSE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
#[java_packet(PLAY_COOKIE_RESPONSE)] #[java_packet(PLAY_COOKIE_RESPONSE)]
/// Response to a `CCookieRequest` (play) from the server. /// Response to a `CCookieRequest` (play) from the server.
/// The Notchian (vanilla) server only accepts responses of up to 5 KiB in size. /// The Notchian (vanilla) server only accepts responses of up to 5 KiB in size.
pub struct SCookieResponse { pub struct SCookieResponse<'a> {
pub key: Box<str>, pub key: &'a str,
pub payload: Option<Box<[u8]>>, // 5120, pub payload: Option<&'a [u8]>, // 5120,
} }
const MAX_COOKIE_LENGTH: usize = 5120; const MAX_COOKIE_LENGTH: usize = 5120;
impl ServerPacket for SCookieResponse { impl<'a> ServerPacket<'a> for SCookieResponse<'a> {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
let key = read.get_str()?; let key = read.get_str_borrowed()?;
let has_payload = read.get_bool()?; let has_payload = read.get_bool()?;
if !has_payload { if !has_payload {
@ -33,7 +31,7 @@ impl ServerPacket for SCookieResponse {
return Err(ReadingError::TooLarge("SCookieResponse".to_string())); return Err(ReadingError::TooLarge("SCookieResponse".to_string()));
} }
let payload = read.read_boxed_slice(payload_length)?; let payload = read.read_slice_borrowed(payload_length)?;
Ok(Self { Ok(Self {
key, key,

View File

@ -1,24 +1,22 @@
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_data::packet::serverbound::PLAY_CUSTOM_CLICK_ACTION; use pumpkin_data::packet::serverbound::PLAY_CUSTOM_CLICK_ACTION;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::resource_location::ResourceLocation;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_CUSTOM_CLICK_ACTION)] #[java_packet(PLAY_CUSTOM_CLICK_ACTION)]
pub struct SCustomClickAction { pub struct SCustomClickAction<'a> {
pub action_id: ResourceLocation, pub action_id: &'a str,
pub payload: Option<Box<[u8]>>, pub payload: Option<&'a [u8]>,
} }
impl ServerPacket for SCustomClickAction { impl<'a> ServerPacket<'a> for SCustomClickAction<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
action_id: bytebuf.get_str()?.to_string(), action_id: bytebuf.get_str_borrowed()?,
payload: bytebuf.get_option(|b| b.read_remaining_to_boxed_slice(32767))?, payload: bytebuf.get_option(|b| b.read_remaining_slice_borrowed(32767))?,
}) })
} }
} }

View File

@ -1,10 +1,8 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::PLAY_CUSTOM_PAYLOAD; use pumpkin_data::packet::serverbound::PLAY_CUSTOM_PAYLOAD;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use crate::{ReadingError, ServerPacket, ser::NetworkReadExt}; use crate::{ReadingError, ServerPacket, ser::NetworkReadSliceExt};
/// The maximum allowed size for a play custom payload (32 KiB). /// The maximum allowed size for a play custom payload (32 KiB).
const MAX_PAYLOAD_SIZE: usize = 32_767; const MAX_PAYLOAD_SIZE: usize = 32_767;
@ -14,19 +12,19 @@ const MAX_PAYLOAD_SIZE: usize = 32_767;
/// This allows mods, plugins, or proxy software to send proprietary data over the standard /// This allows mods, plugins, or proxy software to send proprietary data over the standard
/// Minecraft protocol. /// Minecraft protocol.
#[java_packet(PLAY_CUSTOM_PAYLOAD)] #[java_packet(PLAY_CUSTOM_PAYLOAD)]
pub struct SCustomPayload { pub struct SCustomPayload<'a> {
/// The name of the channel used to distinguish different types of messages. /// The name of the channel used to distinguish different types of messages.
/// Example: `minecraft:brand` or `voicechat:request_secret`. /// Example: `minecraft:brand` or `voicechat:request_secret`.
pub channel: Box<str>, pub channel: &'a str,
/// The payload sent by the client. /// The payload sent by the client.
pub data: Box<[u8]>, pub data: &'a [u8],
} }
impl ServerPacket for SCustomPayload { impl<'a> ServerPacket<'a> for SCustomPayload<'a> {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
channel: read.get_str()?, channel: read.get_str_borrowed()?,
data: read.read_remaining_to_boxed_slice(MAX_PAYLOAD_SIZE)?, data: read.read_remaining_slice_borrowed(MAX_PAYLOAD_SIZE)?,
}) })
} }
} }

View File

@ -1,5 +1,3 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::PLAY_INTERACT; use pumpkin_data::packet::serverbound::PLAY_INTERACT;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::{math::vector3::Vector3, version::JavaMinecraftVersion}; use pumpkin_util::{math::vector3::Vector3, version::JavaMinecraftVersion};
@ -20,8 +18,8 @@ pub struct SInteract {
} }
// Great job Mojang ;D // Great job Mojang ;D
impl ServerPacket for SInteract { impl<'a> ServerPacket<'a> for SInteract {
fn read(mut read: impl Read, version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(mut read: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
// 26.1+ removes the 'type' field and uses doubles for location // 26.1+ removes the 'type' field and uses doubles for location
if version >= &JavaMinecraftVersion::V_26_1 { if version >= &JavaMinecraftVersion::V_26_1 {
let entity_id = read.get_var_int()?; let entity_id = read.get_var_int()?;

View File

@ -6,7 +6,6 @@ use pumpkin_data::packet::serverbound::PLAY_JIGSAW_GENERATE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::codec::var_int::VarInt; use crate::codec::var_int::VarInt;
@ -17,9 +16,9 @@ pub struct SJigsawGenerate {
pub keep_jigsaws: bool, pub keep_jigsaws: bool,
} }
impl ServerPacket for SJigsawGenerate { impl<'a> ServerPacket<'a> for SJigsawGenerate {
fn read( fn read(
mut bytebuf: impl Read, bytebuf: &mut &'a [u8],
_protocol_version: &JavaMinecraftVersion, _protocol_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {

View File

@ -6,15 +6,14 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_KEEP_ALIVE)] #[java_packet(PLAY_KEEP_ALIVE)]
pub struct SKeepAlive { pub struct SKeepAlive {
pub keep_alive_id: i64, pub keep_alive_id: i64,
} }
impl ServerPacket for SKeepAlive { impl<'a> ServerPacket<'a> for SKeepAlive {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
keep_alive_id: bytebuf.get_i64_be()?, keep_alive_id: bytebuf.get_i64_be()?,
}) })

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_MOVE_VEHICLE; use pumpkin_data::packet::serverbound::PLAY_MOVE_VEHICLE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_MOVE_VEHICLE)] #[java_packet(PLAY_MOVE_VEHICLE)]
pub struct SMoveVehicle { pub struct SMoveVehicle {
@ -16,9 +15,9 @@ pub struct SMoveVehicle {
pub pitch: f32, pub pitch: f32,
} }
impl ServerPacket for SMoveVehicle { impl<'a> ServerPacket<'a> for SMoveVehicle {
fn read( fn read(
mut bytebuf: impl Read, bytebuf: &mut &'a [u8],
_protocol_version: &JavaMinecraftVersion, _protocol_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {

View File

@ -6,7 +6,6 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_PADDLE_BOAT)] #[java_packet(PLAY_PADDLE_BOAT)]
pub struct SPaddleBoat { pub struct SPaddleBoat {
@ -14,8 +13,8 @@ pub struct SPaddleBoat {
pub right_paddle: bool, pub right_paddle: bool,
} }
impl ServerPacket for SPaddleBoat { impl<'a> ServerPacket<'a> for SPaddleBoat {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
left_paddle: bytebuf.get_bool()?, left_paddle: bytebuf.get_bool()?,
right_paddle: bytebuf.get_bool()?, right_paddle: bytebuf.get_bool()?,

View File

@ -8,7 +8,6 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_PICK_ITEM_FROM_BLOCK)] #[java_packet(PLAY_PICK_ITEM_FROM_BLOCK)]
pub struct SPickItemFromBlock { pub struct SPickItemFromBlock {
@ -16,8 +15,8 @@ pub struct SPickItemFromBlock {
pub include_data: bool, pub include_data: bool,
} }
impl ServerPacket for SPickItemFromBlock { impl<'a> ServerPacket<'a> for SPickItemFromBlock {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
pos: BlockPos::from_i64(bytebuf.get_i64_be()?), pos: BlockPos::from_i64(bytebuf.get_i64_be()?),
include_data: bytebuf.get_bool()?, include_data: bytebuf.get_bool()?,
@ -31,8 +30,8 @@ pub struct SPickItemFromEntity {
pub include_data: bool, pub include_data: bool,
} }
impl ServerPacket for SPickItemFromEntity { impl<'a> ServerPacket<'a> for SPickItemFromEntity {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
id: bytebuf.get_var_int()?, id: bytebuf.get_var_int()?,
include_data: bytebuf.get_bool()?, include_data: bytebuf.get_bool()?,

View File

@ -6,15 +6,14 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_PING_REQUEST)] #[java_packet(PLAY_PING_REQUEST)]
pub struct SPlayPingRequest { pub struct SPlayPingRequest {
pub payload: i64, pub payload: i64,
} }
impl ServerPacket for SPlayPingRequest { impl<'a> ServerPacket<'a> for SPlayPingRequest {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
payload: bytebuf.get_i64_be()?, payload: bytebuf.get_i64_be()?,
}) })

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_PLACE_RECIPE; use pumpkin_data::packet::serverbound::PLAY_PLACE_RECIPE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -16,8 +15,8 @@ pub struct SPlaceRecipe {
pub use_max_items: bool, pub use_max_items: bool,
} }
impl ServerPacket for SPlaceRecipe { impl<'a> ServerPacket<'a> for SPlaceRecipe {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
container_id: bytebuf.get_i8()?, container_id: bytebuf.get_i8()?,
recipe_display_id: bytebuf.get_var_int()?, recipe_display_id: bytebuf.get_var_int()?,

View File

@ -8,15 +8,14 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_PLAYER_ABILITIES)] #[java_packet(PLAY_PLAYER_ABILITIES)]
pub struct SPlayerAbilities { pub struct SPlayerAbilities {
pub flags: i8, pub flags: i8,
} }
impl ServerPacket for SPlayerAbilities { impl<'a> ServerPacket<'a> for SPlayerAbilities {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
flags: bytebuf.get_i8()?, flags: bytebuf.get_i8()?,
}) })

View File

@ -6,7 +6,6 @@ use pumpkin_data::packet::serverbound::PLAY_PLAYER_ACTION;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -18,9 +17,9 @@ pub struct SPlayerAction {
pub sequence: VarInt, pub sequence: VarInt,
} }
impl ServerPacket for SPlayerAction { impl<'a> ServerPacket<'a> for SPlayerAction {
fn read( fn read(
mut bytebuf: impl Read, bytebuf: &mut &'a [u8],
_protocol_version: &JavaMinecraftVersion, _protocol_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {

View File

@ -1,5 +1,3 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::PLAY_PLAYER_COMMAND; use pumpkin_data::packet::serverbound::PLAY_PLAYER_COMMAND;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
@ -33,8 +31,8 @@ pub enum Action {
pub struct InvalidAction; pub struct InvalidAction;
impl ServerPacket for SPlayerCommand { impl<'a> ServerPacket<'a> for SPlayerCommand {
fn read(mut read: impl Read, version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
let entity_id = read.get_var_int()?; let entity_id = read.get_var_int()?;
let action_id = read.get_var_int()?; let action_id = read.get_var_int()?;
let jump_boost = read.get_var_int()?; let jump_boost = read.get_var_int()?;

View File

@ -5,15 +5,14 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_MOVE_PLAYER_STATUS_ONLY; use pumpkin_data::packet::serverbound::PLAY_MOVE_PLAYER_STATUS_ONLY;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_MOVE_PLAYER_STATUS_ONLY)] #[java_packet(PLAY_MOVE_PLAYER_STATUS_ONLY)]
pub struct SSetPlayerGround { pub struct SSetPlayerGround {
pub on_ground: bool, pub on_ground: bool,
} }
impl ServerPacket for SSetPlayerGround { impl<'a> ServerPacket<'a> for SSetPlayerGround {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
on_ground: bytebuf.get_bool()?, on_ground: bytebuf.get_bool()?,
}) })

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_PLAYER_INPUT; use pumpkin_data::packet::serverbound::PLAY_PLAYER_INPUT;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_PLAYER_INPUT)] #[java_packet(PLAY_PLAYER_INPUT)]
pub struct SPlayerInput { pub struct SPlayerInput {
@ -23,8 +22,8 @@ impl SPlayerInput {
pub const SPRINT: i8 = 64; pub const SPRINT: i8 = 64;
} }
impl ServerPacket for SPlayerInput { impl<'a> ServerPacket<'a> for SPlayerInput {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
input: bytebuf.get_i8()?, input: bytebuf.get_i8()?,
}) })

View File

@ -2,14 +2,13 @@ use crate::{ServerPacket, ser::ReadingError};
use pumpkin_data::packet::serverbound::PLAY_PLAYER_LOADED; use pumpkin_data::packet::serverbound::PLAY_PLAYER_LOADED;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_PLAYER_LOADED)] #[java_packet(PLAY_PLAYER_LOADED)]
pub struct SPlayerLoaded; pub struct SPlayerLoaded;
impl ServerPacket for SPlayerLoaded { impl<'a> ServerPacket<'a> for SPlayerLoaded {
fn read( fn read(
_bytebuf: impl Read, _bytebuf: &mut &'a [u8],
_protocol_version: &JavaMinecraftVersion, _protocol_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
Ok(Self) Ok(Self)

View File

@ -7,7 +7,6 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_MOVE_PLAYER_POS)] #[java_packet(PLAY_MOVE_PLAYER_POS)]
pub struct SPlayerPosition { pub struct SPlayerPosition {
@ -16,8 +15,8 @@ pub struct SPlayerPosition {
pub collision: u8, pub collision: u8,
} }
impl ServerPacket for SPlayerPosition { impl<'a> ServerPacket<'a> for SPlayerPosition {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
position: Vector3::new( position: Vector3::new(
bytebuf.get_f64_be()?, bytebuf.get_f64_be()?,

View File

@ -7,7 +7,6 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
pub const FLAG_ON_GROUND: u8 = 0x01; pub const FLAG_ON_GROUND: u8 = 0x01;
pub const FLAG_IN_WALL: u8 = 0x02; pub const FLAG_IN_WALL: u8 = 0x02;
@ -21,8 +20,8 @@ pub struct SPlayerPositionRotation {
pub collision: u8, pub collision: u8,
} }
impl ServerPacket for SPlayerPositionRotation { impl<'a> ServerPacket<'a> for SPlayerPositionRotation {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
position: Vector3::new( position: Vector3::new(
bytebuf.get_f64_be()?, bytebuf.get_f64_be()?,

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_MOVE_PLAYER_ROT; use pumpkin_data::packet::serverbound::PLAY_MOVE_PLAYER_ROT;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_MOVE_PLAYER_ROT)] #[java_packet(PLAY_MOVE_PLAYER_ROT)]
pub struct SPlayerRotation { pub struct SPlayerRotation {
@ -14,8 +13,8 @@ pub struct SPlayerRotation {
pub ground: bool, pub ground: bool,
} }
impl ServerPacket for SPlayerRotation { impl<'a> ServerPacket<'a> for SPlayerRotation {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
yaw: bytebuf.get_f32_be()?, yaw: bytebuf.get_f32_be()?,
pitch: bytebuf.get_f32_be()?, pitch: bytebuf.get_f32_be()?,

View File

@ -1,5 +1,3 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::PLAY_CHAT_SESSION_UPDATE; use pumpkin_data::packet::serverbound::PLAY_CHAT_SESSION_UPDATE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
@ -18,22 +16,24 @@ pub struct SPlayerSession {
pub key_signature: Box<[u8]>, pub key_signature: Box<[u8]>,
} }
impl ServerPacket for SPlayerSession { impl<'a> ServerPacket<'a> for SPlayerSession {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
let session_id = read.get_uuid()?; let session_id = read.get_uuid()?;
let expires_at = read.get_i64_be()?; let expires_at = read.get_i64_be()?;
let public_key_length = read.get_var_int()?.0 as usize; let public_key_length = read.get_var_int()?.0 as usize;
let public_key = read.read_boxed_slice(public_key_length)?; let mut public_key = vec![0u8; public_key_length];
read.read_bytes_to_buf(&mut public_key)?;
let key_signature_length = read.get_var_int()?.0 as usize; let key_signature_length = read.get_var_int()?.0 as usize;
let key_signature = read.read_boxed_slice(key_signature_length)?; let mut key_signature = vec![0u8; key_signature_length];
read.read_bytes_to_buf(&mut key_signature)?;
Ok(Self { Ok(Self {
session_id, session_id,
expires_at, expires_at,
public_key, public_key: public_key.into_boxed_slice(),
key_signature, key_signature: key_signature.into_boxed_slice(),
}) })
} }
} }

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_RECIPE_BOOK_CHANGE_SETTINGS; use pumpkin_data::packet::serverbound::PLAY_RECIPE_BOOK_CHANGE_SETTINGS;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -16,8 +15,8 @@ pub struct SRecipeBookChangeSettings {
pub is_filtering: bool, pub is_filtering: bool,
} }
impl ServerPacket for SRecipeBookChangeSettings { impl<'a> ServerPacket<'a> for SRecipeBookChangeSettings {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
book_type: bytebuf.get_var_int()?, book_type: bytebuf.get_var_int()?,
is_open: bytebuf.get_bool()?, is_open: bytebuf.get_bool()?,

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_RECIPE_BOOK_SEEN_RECIPE; use pumpkin_data::packet::serverbound::PLAY_RECIPE_BOOK_SEEN_RECIPE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -14,8 +13,8 @@ pub struct SRecipeBookSeenRecipe {
pub recipe_display_id: VarInt, pub recipe_display_id: VarInt,
} }
impl ServerPacket for SRecipeBookSeenRecipe { impl<'a> ServerPacket<'a> for SRecipeBookSeenRecipe {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
recipe_display_id: bytebuf.get_var_int()?, recipe_display_id: bytebuf.get_var_int()?,
}) })

View File

@ -1,22 +1,21 @@
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_data::packet::serverbound::PLAY_RENAME_ITEM; use pumpkin_data::packet::serverbound::PLAY_RENAME_ITEM;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[derive(Debug)] #[derive(Debug)]
#[java_packet(PLAY_RENAME_ITEM)] #[java_packet(PLAY_RENAME_ITEM)]
pub struct SRenameItem { pub struct SRenameItem<'a> {
pub item_name: String, pub item_name: &'a str,
} }
impl ServerPacket for SRenameItem { impl<'a> ServerPacket<'a> for SRenameItem<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
item_name: bytebuf.get_str_bounded(32767)?.into_string(), item_name: bytebuf.get_str_bounded_borrowed(32767)?,
}) })
} }
} }

View File

@ -1,12 +1,11 @@
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_data::packet::serverbound::PLAY_SEEN_ADVANCEMENTS; use pumpkin_data::packet::serverbound::PLAY_SEEN_ADVANCEMENTS;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::identifier::Identifier; use pumpkin_util::identifier::Identifier;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[derive(Debug)] #[derive(Debug)]
#[java_packet(PLAY_SEEN_ADVANCEMENTS)] #[java_packet(PLAY_SEEN_ADVANCEMENTS)]
@ -15,14 +14,14 @@ pub enum SSeenAdvancement {
CloseTab, CloseTab,
} }
impl ServerPacket for SSeenAdvancement { impl<'a> ServerPacket<'a> for SSeenAdvancement {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
let action = bytebuf.get_var_int()?; let action = bytebuf.get_var_int()?;
match action.0 { match action.0 {
0 => { 0 => {
let id_str = bytebuf.get_str()?; let id_str = bytebuf.get_str_borrowed()?;
let id = let id =
Identifier::parse(&id_str).map_err(|e| ReadingError::Message(e.to_string()))?; Identifier::parse(id_str).map_err(|e| ReadingError::Message(e.to_string()))?;
Ok(Self::OpenTab(id)) Ok(Self::OpenTab(id))
} }
1 => Ok(Self::CloseTab), 1 => Ok(Self::CloseTab),

View File

@ -8,15 +8,14 @@ use crate::{
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_SELECT_TRADE)] #[java_packet(PLAY_SELECT_TRADE)]
pub struct SSelectTrade { pub struct SSelectTrade {
pub selected_slot: VarInt, pub selected_slot: VarInt,
} }
impl ServerPacket for SSelectTrade { impl<'a> ServerPacket<'a> for SSelectTrade {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
selected_slot: bytebuf.get_var_int()?, selected_slot: bytebuf.get_var_int()?,
}) })

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_SET_BEACON; use pumpkin_data::packet::serverbound::PLAY_SET_BEACON;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::codec::var_int::VarInt; use crate::codec::var_int::VarInt;
@ -15,8 +14,8 @@ pub struct SSetBeacon {
pub secondary_effect: Option<VarInt>, pub secondary_effect: Option<VarInt>,
} }
impl ServerPacket for SSetBeacon { impl<'a> ServerPacket<'a> for SSetBeacon {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
primary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?, primary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,
secondary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?, secondary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,

View File

@ -1,19 +1,18 @@
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_data::packet::serverbound::PLAY_SET_COMMAND_BLOCK; use pumpkin_data::packet::serverbound::PLAY_SET_COMMAND_BLOCK;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::codec::var_int::VarInt; use crate::codec::var_int::VarInt;
#[java_packet(PLAY_SET_COMMAND_BLOCK)] #[java_packet(PLAY_SET_COMMAND_BLOCK)]
pub struct SSetCommandBlock { pub struct SSetCommandBlock<'a> {
pub pos: BlockPos, pub pos: BlockPos,
pub command: String, pub command: &'a str,
pub mode: VarInt, pub mode: VarInt,
/// Operation mode flags /// Operation mode flags
@ -23,11 +22,11 @@ pub struct SSetCommandBlock {
pub flags: i8, pub flags: i8,
} }
impl ServerPacket for SSetCommandBlock { impl<'a> ServerPacket<'a> for SSetCommandBlock<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
pos: BlockPos::from_i64(bytebuf.get_i64_be()?), pos: BlockPos::from_i64(bytebuf.get_i64_be()?),
command: bytebuf.get_str_bounded(32767)?.into_string(), command: bytebuf.get_str_bounded_borrowed(32767)?,
mode: bytebuf.get_var_int()?, mode: bytebuf.get_var_int()?,
flags: bytebuf.get_i8()?, flags: bytebuf.get_i8()?,
}) })

View File

@ -14,9 +14,9 @@ pub struct SSetCreativeSlot {
pub clicked_item: ItemStackSerializer<'static>, pub clicked_item: ItemStackSerializer<'static>,
} }
impl ServerPacket for SSetCreativeSlot { impl<'a> ServerPacket<'a> for SSetCreativeSlot {
fn read( fn read(
mut read: impl std::io::Read, mut read: &mut &'a [u8],
_version: &JavaMinecraftVersion, _version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
let slot = read.get_i16_be()?; let slot = read.get_i16_be()?;

View File

@ -5,15 +5,14 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_SET_CARRIED_ITEM; use pumpkin_data::packet::serverbound::PLAY_SET_CARRIED_ITEM;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_SET_CARRIED_ITEM)] #[java_packet(PLAY_SET_CARRIED_ITEM)]
pub struct SSetHeldItem { pub struct SSetHeldItem {
pub slot: i16, pub slot: i16,
} }
impl ServerPacket for SSetHeldItem { impl<'a> ServerPacket<'a> for SSetHeldItem {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
slot: bytebuf.get_i16_be()?, slot: bytebuf.get_i16_be()?,
}) })

View File

@ -1,36 +1,35 @@
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_data::packet::serverbound::PLAY_SET_JIGSAW_BLOCK; use pumpkin_data::packet::serverbound::PLAY_SET_JIGSAW_BLOCK;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::codec::var_int::VarInt; use crate::codec::var_int::VarInt;
#[java_packet(PLAY_SET_JIGSAW_BLOCK)] #[java_packet(PLAY_SET_JIGSAW_BLOCK)]
pub struct SSetJigsawBlock { pub struct SSetJigsawBlock<'a> {
pub pos: BlockPos, pub pos: BlockPos,
pub name: String, pub name: &'a str,
pub target: String, pub target: &'a str,
pub pool: String, pub pool: &'a str,
pub final_state: String, pub final_state: &'a str,
pub joint: String, pub joint: &'a str,
pub selection_priority: VarInt, pub selection_priority: VarInt,
pub placement_priority: VarInt, pub placement_priority: VarInt,
} }
impl ServerPacket for SSetJigsawBlock { impl<'a> ServerPacket<'a> for SSetJigsawBlock<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
pos: BlockPos::from_i64(bytebuf.get_i64_be()?), pos: BlockPos::from_i64(bytebuf.get_i64_be()?),
name: bytebuf.get_str_bounded(32767)?.into_string(), name: bytebuf.get_str_bounded_borrowed(32767)?,
target: bytebuf.get_str_bounded(32767)?.into_string(), target: bytebuf.get_str_bounded_borrowed(32767)?,
pool: bytebuf.get_str_bounded(32767)?.into_string(), pool: bytebuf.get_str_bounded_borrowed(32767)?,
final_state: bytebuf.get_str_bounded(32767)?.into_string(), final_state: bytebuf.get_str_bounded_borrowed(32767)?,
joint: bytebuf.get_str_bounded(32767)?.into_string(), joint: bytebuf.get_str_bounded_borrowed(32767)?,
selection_priority: bytebuf.get_var_int()?, selection_priority: bytebuf.get_var_int()?,
placement_priority: bytebuf.get_var_int()?, placement_priority: bytebuf.get_var_int()?,
}) })

View File

@ -4,24 +4,27 @@ use pumpkin_util::math::position::BlockPos;
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read; use std::io::Read;
#[java_packet(PLAY_SET_TEST_BLOCK)] #[java_packet(PLAY_SET_TEST_BLOCK)]
pub struct SSetTestBlock { pub struct SSetTestBlock<'a> {
pub position: BlockPos, pub position: BlockPos,
pub mode: TestBlockMode, pub mode: TestBlockMode,
pub message: String, pub message: &'a str,
} }
impl ServerPacket for SSetTestBlock { impl<'a> ServerPacket<'a> for SSetTestBlock<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(
mut bytebuf: &mut &'a [u8],
_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
position: BlockPos::from_i64(bytebuf.get_i64_be()?), position: BlockPos::from_i64(bytebuf.get_i64_be()?),
mode: TestBlockMode::read(&mut bytebuf)?, mode: TestBlockMode::read(&mut bytebuf)?,
message: bytebuf.get_str()?.into_string(), message: bytebuf.get_str_borrowed()?,
}) })
} }
} }

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_SWING; use pumpkin_data::packet::serverbound::PLAY_SWING;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -14,8 +13,8 @@ pub struct SSwingArm {
pub hand: VarInt, pub hand: VarInt,
} }
impl ServerPacket for SSwingArm { impl<'a> ServerPacket<'a> for SSwingArm {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
hand: bytebuf.get_var_int()?, hand: bytebuf.get_var_int()?,
}) })

View File

@ -5,16 +5,15 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_TELEPORT_TO_ENTITY; use pumpkin_data::packet::serverbound::PLAY_TELEPORT_TO_ENTITY;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
#[java_packet(PLAY_TELEPORT_TO_ENTITY)] #[java_packet(PLAY_TELEPORT_TO_ENTITY)]
pub struct STeleportToEntity { pub struct STeleportToEntity {
pub target: uuid::Uuid, pub target: uuid::Uuid,
} }
impl ServerPacket for STeleportToEntity { impl<'a> ServerPacket<'a> for STeleportToEntity {
fn read( fn read(
mut bytebuf: impl Read, bytebuf: &mut &'a [u8],
_protocol_version: &JavaMinecraftVersion, _protocol_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {

View File

@ -12,18 +12,18 @@ use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read; use std::io::Read;
#[java_packet(PLAY_TEST_INSTANCE_BLOCK_ACTION)] #[java_packet(PLAY_TEST_INSTANCE_BLOCK_ACTION)]
pub struct STestInstanceBlockAction { pub struct STestInstanceBlockAction<'a> {
pub pos: BlockPos, pub pos: BlockPos,
pub action: TestInstanceBlockAction, pub action: TestInstanceBlockAction,
pub data: TestInstanceBlockData, pub data: TestInstanceBlockData<'a>,
} }
impl ServerPacket for STestInstanceBlockAction { impl<'a> ServerPacket<'a> for STestInstanceBlockAction<'a> {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
pos: BlockPos::from_i64(bytebuf.get_i64_be()?), pos: BlockPos::from_i64(bytebuf.get_i64_be()?),
action: TestInstanceBlockAction::read(&mut bytebuf)?, action: TestInstanceBlockAction::read(bytebuf)?,
data: TestInstanceBlockData::read(&mut bytebuf)?, data: TestInstanceBlockData::read(bytebuf)?,
}) })
} }
} }
@ -72,18 +72,18 @@ impl VarIntVector3 {
} }
} }
pub struct TestInstanceBlockData { pub struct TestInstanceBlockData<'a> {
pub test: Option<String>, pub test: Option<&'a str>,
pub size: VarIntVector3, pub size: VarIntVector3,
pub rotation: pumpkin_data::block_rotation::Rotation, pub rotation: pumpkin_data::block_rotation::Rotation,
pub ignore_entities: bool, pub ignore_entities: bool,
pub status: TestInstanceBlockStatus, pub status: TestInstanceBlockStatus,
pub error_message: Option<String>, pub error_message: Option<&'a str>,
} }
impl TestInstanceBlockData { impl<'a> TestInstanceBlockData<'a> {
fn read(bytebuf: &mut impl Read) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8]) -> Result<Self, ReadingError> {
let test = bytebuf.get_option(|b| b.get_str().map(String::from))?; let test = bytebuf.get_option(crate::ser::NetworkReadSliceExt::get_str_borrowed)?;
let size = VarIntVector3::read(bytebuf)?; let size = VarIntVector3::read(bytebuf)?;
let rotation = match bytebuf.get_var_int()?.0 { let rotation = match bytebuf.get_var_int()?.0 {
0 => pumpkin_data::block_rotation::Rotation::None, 0 => pumpkin_data::block_rotation::Rotation::None,
@ -104,7 +104,8 @@ impl TestInstanceBlockData {
)); ));
} }
}; };
let error_message = bytebuf.get_option(|b| b.get_str().map(String::from))?; let error_message =
bytebuf.get_option(crate::ser::NetworkReadSliceExt::get_str_borrowed)?;
Ok(Self { Ok(Self {
test, test,

View File

@ -1,35 +1,33 @@
use std::io::Read;
use pumpkin_data::packet::serverbound::PLAY_SIGN_UPDATE; use pumpkin_data::packet::serverbound::PLAY_SIGN_UPDATE;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::{math::position::BlockPos, version::JavaMinecraftVersion}; use pumpkin_util::{math::position::BlockPos, version::JavaMinecraftVersion};
use crate::{ use crate::{
ServerPacket, ServerPacket,
ser::{NetworkReadExt, ReadingError}, ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
}; };
#[java_packet(PLAY_SIGN_UPDATE)] #[java_packet(PLAY_SIGN_UPDATE)]
pub struct SUpdateSign { pub struct SUpdateSign<'a> {
pub location: BlockPos, pub location: BlockPos,
pub is_front_text: bool, pub is_front_text: bool,
pub line_1: Box<str>, pub line_1: &'a str,
pub line_2: Box<str>, pub line_2: &'a str,
pub line_3: Box<str>, pub line_3: &'a str,
pub line_4: Box<str>, pub line_4: &'a str,
} }
const MAX_LINE_LENGTH: usize = 386; const MAX_LINE_LENGTH: usize = 386;
impl ServerPacket for SUpdateSign { impl<'a> ServerPacket<'a> for SUpdateSign<'a> {
fn read(mut read: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
location: BlockPos::from_i64(read.get_i64_be()?), location: BlockPos::from_i64(read.get_i64_be()?),
is_front_text: read.get_bool()?, is_front_text: read.get_bool()?,
line_1: read.get_str_bounded(MAX_LINE_LENGTH)?, line_1: read.get_str_bounded_borrowed(MAX_LINE_LENGTH)?,
line_2: read.get_str_bounded(MAX_LINE_LENGTH)?, line_2: read.get_str_bounded_borrowed(MAX_LINE_LENGTH)?,
line_3: read.get_str_bounded(MAX_LINE_LENGTH)?, line_3: read.get_str_bounded_borrowed(MAX_LINE_LENGTH)?,
line_4: read.get_str_bounded(MAX_LINE_LENGTH)?, line_4: read.get_str_bounded_borrowed(MAX_LINE_LENGTH)?,
}) })
} }
} }

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::PLAY_USE_ITEM; use pumpkin_data::packet::serverbound::PLAY_USE_ITEM;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -18,9 +17,9 @@ pub struct SUseItem {
pub pitch: f32, pub pitch: f32,
} }
impl ServerPacket for SUseItem { impl<'a> ServerPacket<'a> for SUseItem {
fn read( fn read(
mut bytebuf: impl Read, bytebuf: &mut &'a [u8],
_protocol_version: &JavaMinecraftVersion, _protocol_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {

View File

@ -6,7 +6,6 @@ use pumpkin_data::packet::serverbound::PLAY_USE_ITEM_ON;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
use crate::VarInt; use crate::VarInt;
@ -21,8 +20,8 @@ pub struct SUseItemOn {
pub sequence: VarInt, pub sequence: VarInt,
} }
impl ServerPacket for SUseItemOn { impl<'a> ServerPacket<'a> for SUseItemOn {
fn read(mut bytebuf: impl Read, _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> { fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {
hand: bytebuf.get_var_int()?, hand: bytebuf.get_var_int()?,
position: BlockPos::from_i64(bytebuf.get_i64_be()?), position: BlockPos::from_i64(bytebuf.get_i64_be()?),

View File

@ -5,7 +5,6 @@ use crate::{
use pumpkin_data::packet::serverbound::STATUS_PING_REQUEST; use pumpkin_data::packet::serverbound::STATUS_PING_REQUEST;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
/// Sent by the client to measure the round-trip time (latency) to the server. /// Sent by the client to measure the round-trip time (latency) to the server.
/// ///
@ -16,9 +15,9 @@ pub struct SStatusPingRequest {
pub payload: i64, pub payload: i64,
} }
impl ServerPacket for SStatusPingRequest { impl<'a> ServerPacket<'a> for SStatusPingRequest {
fn read( fn read(
mut bytebuf: impl Read, bytebuf: &mut &'a [u8],
_protocol_version: &JavaMinecraftVersion, _protocol_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
Ok(Self { Ok(Self {

View File

@ -2,7 +2,6 @@ use crate::{ServerPacket, ser::ReadingError};
use pumpkin_data::packet::serverbound::STATUS_STATUS_REQUEST; use pumpkin_data::packet::serverbound::STATUS_STATUS_REQUEST;
use pumpkin_macros::java_packet; use pumpkin_macros::java_packet;
use pumpkin_util::version::JavaMinecraftVersion; use pumpkin_util::version::JavaMinecraftVersion;
use std::io::Read;
/// Sent by the client to request the server's current status information. /// Sent by the client to request the server's current status information.
/// ///
@ -11,9 +10,9 @@ use std::io::Read;
#[java_packet(STATUS_STATUS_REQUEST)] #[java_packet(STATUS_STATUS_REQUEST)]
pub struct SStatusRequest; pub struct SStatusRequest;
impl ServerPacket for SStatusRequest { impl<'a> ServerPacket<'a> for SStatusRequest {
fn read( fn read(
_bytebuf: impl Read, _bytebuf: &mut &'a [u8],
_protocol_version: &JavaMinecraftVersion, _protocol_version: &JavaMinecraftVersion,
) -> Result<Self, ReadingError> { ) -> Result<Self, ReadingError> {
Ok(Self) Ok(Self)

View File

@ -253,8 +253,8 @@ pub trait ClientPacket: MultiVersionJavaPacket {
) -> Result<(), WritingError>; ) -> Result<(), WritingError>;
} }
pub trait ServerPacket: MultiVersionJavaPacket + Sized { pub trait ServerPacket<'a>: MultiVersionJavaPacket + Sized {
fn read(read: impl Read, version: &JavaMinecraftVersion) -> Result<Self, ReadingError>; fn read(read: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError>;
} }
pub trait BClientPacket: Packet { pub trait BClientPacket: Packet {

View File

@ -1,4 +1,5 @@
use core::str; use core::str;
use std::borrow::Cow;
use std::io::{Read, Write}; use std::io::{Read, Write};
use crate::{ use crate::{
@ -61,34 +62,40 @@ pub trait NetworkReadExt {
fn get_i128_be(&mut self) -> Result<i128, ReadingError>; fn get_i128_be(&mut self) -> Result<i128, ReadingError>;
fn get_u128_be(&mut self) -> Result<u128, ReadingError>; fn get_u128_be(&mut self) -> Result<u128, ReadingError>;
#[inline]
fn get_i16(&mut self) -> Result<i16, ReadingError> { fn get_i16(&mut self) -> Result<i16, ReadingError> {
self.get_i16_be() self.get_i16_be()
} }
#[inline]
fn get_u16(&mut self) -> Result<u16, ReadingError> { fn get_u16(&mut self) -> Result<u16, ReadingError> {
self.get_u16_be() self.get_u16_be()
} }
#[inline]
fn get_i32(&mut self) -> Result<i32, ReadingError> { fn get_i32(&mut self) -> Result<i32, ReadingError> {
self.get_i32_be() self.get_i32_be()
} }
#[inline]
fn get_u32(&mut self) -> Result<u32, ReadingError> { fn get_u32(&mut self) -> Result<u32, ReadingError> {
self.get_u32_be() self.get_u32_be()
} }
#[inline]
fn get_i64(&mut self) -> Result<i64, ReadingError> { fn get_i64(&mut self) -> Result<i64, ReadingError> {
self.get_i64_be() self.get_i64_be()
} }
#[inline]
fn get_u64(&mut self) -> Result<u64, ReadingError> { fn get_u64(&mut self) -> Result<u64, ReadingError> {
self.get_u64_be() self.get_u64_be()
} }
#[inline]
fn get_f32(&mut self) -> Result<f32, ReadingError> { fn get_f32(&mut self) -> Result<f32, ReadingError> {
self.get_f32_be() self.get_f32_be()
} }
#[inline]
fn get_f64(&mut self) -> Result<f64, ReadingError> { fn get_f64(&mut self) -> Result<f64, ReadingError> {
self.get_f64_be() self.get_f64_be()
} }
fn read_boxed_slice(&mut self, count: usize) -> Result<Box<[u8]>, ReadingError>; fn read_bytes_to_buf(&mut self, buf: &mut [u8]) -> Result<(), ReadingError>;
fn read_remaining_to_boxed_slice(&mut self, bound: usize) -> Result<Box<[u8]>, ReadingError>;
fn get_bool(&mut self) -> Result<bool, ReadingError>; fn get_bool(&mut self) -> Result<bool, ReadingError>;
fn get_var_int(&mut self) -> Result<VarInt, ReadingError>; fn get_var_int(&mut self) -> Result<VarInt, ReadingError>;
@ -96,23 +103,154 @@ pub trait NetworkReadExt {
fn get_var_long(&mut self) -> Result<VarLong, ReadingError>; fn get_var_long(&mut self) -> Result<VarLong, ReadingError>;
fn get_var_ulong(&mut self) -> Result<VarULong, ReadingError>; fn get_var_ulong(&mut self) -> Result<VarULong, ReadingError>;
fn get_str_bounded(&mut self, bound: usize) -> Result<Box<str>, ReadingError>; fn get_str_bounded(&mut self, bound: usize) -> Result<Box<str>, ReadingError>;
fn get_str(&mut self) -> Result<Box<str>, ReadingError>; #[inline]
fn get_str(&mut self) -> Result<Box<str>, ReadingError> {
self.get_str_bounded(i32::MAX as usize)
}
fn get_uuid(&mut self) -> Result<uuid::Uuid, ReadingError>; fn get_uuid(&mut self) -> Result<uuid::Uuid, ReadingError>;
fn get_fixed_bitset(&mut self, bits: usize) -> Result<FixedBitSet, ReadingError>; fn get_fixed_bitset(&mut self, bits: usize) -> Result<FixedBitSet, ReadingError>;
#[inline]
fn get_option<G>( fn get_option<G>(
&mut self, &mut self,
parse: impl FnOnce(&mut Self) -> Result<G, ReadingError>, parse: impl FnOnce(&mut Self) -> Result<G, ReadingError>,
) -> Result<Option<G>, ReadingError>; ) -> Result<Option<G>, ReadingError> {
if self.get_bool()? {
Ok(Some(parse(self)?))
} else {
Ok(None)
}
}
#[inline]
fn get_list<G>( fn get_list<G>(
&mut self, &mut self,
parse: impl Fn(&mut Self) -> Result<G, ReadingError>, parse: impl Fn(&mut Self) -> Result<G, ReadingError>,
) -> Result<Vec<G>, ReadingError>; ) -> Result<Vec<G>, ReadingError> {
const MAX_LIST_SIZE: usize = 65536;
let len = self.get_var_int()?.0 as usize;
if len > MAX_LIST_SIZE {
return Err(ReadingError::TooLarge(format!(
"List length {len} exceeds limit"
)));
}
let mut list = Vec::with_capacity(len);
for _ in 0..len {
list.push(parse(self)?);
}
Ok(list)
}
}
pub trait NetworkReadSliceExt<'a> {
fn get_str_borrowed(&mut self) -> Result<&'a str, ReadingError>;
fn get_str_bounded_borrowed(&mut self, bound: usize) -> Result<&'a str, ReadingError>;
fn read_slice_borrowed(&mut self, count: usize) -> Result<&'a [u8], ReadingError>;
fn read_remaining_slice_borrowed(&mut self, bound: usize) -> Result<&'a [u8], ReadingError>;
#[inline]
fn read_cow_slice_borrowed(&mut self, count: usize) -> Result<Cow<'a, [u8]>, ReadingError> {
Ok(Cow::Borrowed(self.read_slice_borrowed(count)?))
}
#[inline]
fn read_remaining_to_cow_slice_borrowed(
&mut self,
bound: usize,
) -> Result<Cow<'a, [u8]>, ReadingError> {
Ok(Cow::Borrowed(self.read_remaining_slice_borrowed(bound)?))
}
#[inline]
fn get_cow_str_borrowed(&mut self) -> Result<Cow<'a, str>, ReadingError> {
self.get_cow_str_bounded_borrowed(i32::MAX as usize)
}
#[inline]
fn get_cow_str_bounded_borrowed(&mut self, bound: usize) -> Result<Cow<'a, str>, ReadingError> {
Ok(Cow::Borrowed(self.get_str_bounded_borrowed(bound)?))
}
}
impl<'a> NetworkReadSliceExt<'a> for &'a [u8] {
#[inline]
fn read_slice_borrowed(&mut self, count: usize) -> Result<&'a [u8], ReadingError> {
if self.len() < count {
return Err(ReadingError::Incomplete(format!(
"EOF, Tried to read {count} bytes but only {} bytes left",
self.len()
)));
}
let (head, tail) = self.split_at(count);
*self = tail;
Ok(head)
}
#[inline]
fn read_remaining_slice_borrowed(&mut self, bound: usize) -> Result<&'a [u8], ReadingError> {
if self.len() > bound {
return Err(ReadingError::TooLarge(
"Read remaining too long".to_string(),
));
}
let slice = *self;
*self = &[];
Ok(slice)
}
#[inline]
fn get_str_bounded_borrowed(&mut self, bound: usize) -> Result<&'a str, ReadingError> {
let bytes_len = self.get_var_uint()?.0 as usize;
let maximum_utf8_bytes = bound.saturating_mul(3);
if bytes_len > maximum_utf8_bytes {
return Err(ReadingError::TooLarge(format!(
"string has too many bytes ({bytes_len} > {maximum_utf8_bytes})"
)));
}
let bytes = self.read_slice_borrowed(bytes_len)?;
let string =
std::str::from_utf8(bytes).map_err(|e| ReadingError::Message(e.to_string()))?;
if string.encode_utf16().nth(bound).is_some() {
return Err(ReadingError::TooLarge(format!(
"string has too many UTF-16 characters (more than the maximum limit {bound})"
)));
}
Ok(string)
}
#[inline]
fn get_str_borrowed(&mut self) -> Result<&'a str, ReadingError> {
self.get_str_bounded_borrowed(i32::MAX as usize)
}
}
impl<'a, R: NetworkReadSliceExt<'a> + ?Sized> NetworkReadSliceExt<'a> for &mut R {
#[inline]
fn get_str_borrowed(&mut self) -> Result<&'a str, ReadingError> {
(**self).get_str_borrowed()
}
#[inline]
fn get_str_bounded_borrowed(&mut self, bound: usize) -> Result<&'a str, ReadingError> {
(**self).get_str_bounded_borrowed(bound)
}
#[inline]
fn read_slice_borrowed(&mut self, count: usize) -> Result<&'a [u8], ReadingError> {
(**self).read_slice_borrowed(count)
}
#[inline]
fn read_remaining_slice_borrowed(&mut self, bound: usize) -> Result<&'a [u8], ReadingError> {
(**self).read_remaining_slice_borrowed(bound)
}
} }
macro_rules! get_number_be { macro_rules! get_number_be {
($name:ident, $type:ty) => { ($name:ident, $type:ty) => {
#[inline]
fn $name(&mut self) -> Result<$type, ReadingError> { fn $name(&mut self) -> Result<$type, ReadingError> {
let mut buf = [0u8; std::mem::size_of::<$type>()]; let mut buf = [0u8; std::mem::size_of::<$type>()];
self.read_exact(&mut buf) self.read_exact(&mut buf)
@ -137,58 +275,38 @@ impl<R: Read> NetworkReadExt for R {
get_number_be!(get_f32_be, f32); get_number_be!(get_f32_be, f32);
get_number_be!(get_f64_be, f64); get_number_be!(get_f64_be, f64);
fn read_boxed_slice(&mut self, length: usize) -> Result<Box<[u8]>, ReadingError> { #[inline]
// Increase this to at least 2MB to handle larger Bedrock batches fn read_bytes_to_buf(&mut self, buf: &mut [u8]) -> Result<(), ReadingError> {
const MAX_SLICE_LENGTH: usize = 2 * 1024 * 1024; // 2MB self.read_exact(buf)
if !(0..=MAX_SLICE_LENGTH).contains(&length) { .map_err(|err| ReadingError::Incomplete(err.to_string()))
return Err(ReadingError::Message(format!(
"read_boxed_slice: length {length} out of bounds"
)));
}
let mut buf = vec![0u8; length];
self.read_exact(&mut buf)
.map_err(|err| ReadingError::Incomplete(err.to_string()))?;
Ok(buf.into())
}
fn read_remaining_to_boxed_slice(&mut self, bound: usize) -> Result<Box<[u8]>, ReadingError> {
let mut return_buf = Vec::new();
// Take one extra byte to check for exceeding bound
self.take(bound as u64 + 1)
.read_to_end(&mut return_buf)
.map_err(|err| ReadingError::Incomplete(err.to_string()))?;
if return_buf.len() > bound {
return Err(ReadingError::TooLarge(
"Read remaining too long".to_string(),
));
}
Ok(return_buf.into_boxed_slice())
} }
#[inline]
fn get_bool(&mut self) -> Result<bool, ReadingError> { fn get_bool(&mut self) -> Result<bool, ReadingError> {
let byte = self.get_u8()?; let byte = self.get_u8()?;
Ok(byte != 0) Ok(byte != 0)
} }
#[inline]
fn get_var_int(&mut self) -> Result<VarInt, ReadingError> { fn get_var_int(&mut self) -> Result<VarInt, ReadingError> {
VarInt::decode(self) VarInt::decode(self)
} }
#[inline]
fn get_var_uint(&mut self) -> Result<VarUInt, ReadingError> { fn get_var_uint(&mut self) -> Result<VarUInt, ReadingError> {
VarUInt::decode(self) VarUInt::decode(self)
} }
#[inline]
fn get_var_long(&mut self) -> Result<VarLong, ReadingError> { fn get_var_long(&mut self) -> Result<VarLong, ReadingError> {
VarLong::decode(self) VarLong::decode(self)
} }
#[inline]
fn get_var_ulong(&mut self) -> Result<VarULong, ReadingError> { fn get_var_ulong(&mut self) -> Result<VarULong, ReadingError> {
VarULong::decode(self) VarULong::decode(self)
} }
#[inline]
fn get_str_bounded(&mut self, bound: usize) -> Result<Box<str>, ReadingError> { fn get_str_bounded(&mut self, bound: usize) -> Result<Box<str>, ReadingError> {
let bytes_len = self.get_var_uint()?.0 as usize; let bytes_len = self.get_var_uint()?.0 as usize;
@ -203,11 +321,28 @@ impl<R: Read> NetworkReadExt for R {
))); )));
} }
let data = self.read_boxed_slice(bytes_len)?; if bytes_len <= 128 {
let mut stack_buf = [0u8; 128];
let slice = &mut stack_buf[..bytes_len];
self.read_exact(slice)
.map_err(|err| ReadingError::Incomplete(err.to_string()))?;
let string =
std::str::from_utf8(slice).map_err(|e| ReadingError::Message(e.to_string()))?;
if string.encode_utf16().nth(bound).is_some() {
return Err(ReadingError::TooLarge(format!(
"string has too many UTF-16 characters (more than the maximum limit {bound})"
)));
}
Ok(string.into())
} else {
let mut data = vec![0u8; bytes_len];
self.read_bytes_to_buf(&mut data)?;
let string = let string =
std::str::from_utf8(&data).map_err(|e| ReadingError::Message(e.to_string()))?; std::str::from_utf8(&data).map_err(|e| ReadingError::Message(e.to_string()))?;
// Next, if we're able to find the (bound + 1)th UTF-16 character, the string is too big.
if string.encode_utf16().nth(bound).is_some() { if string.encode_utf16().nth(bound).is_some() {
return Err(ReadingError::TooLarge(format!( return Err(ReadingError::TooLarge(format!(
"string has too many UTF-16 characters (more than the maximum limit {bound})" "string has too many UTF-16 characters (more than the maximum limit {bound})"
@ -216,11 +351,9 @@ impl<R: Read> NetworkReadExt for R {
Ok(string.into()) Ok(string.into())
} }
fn get_str(&mut self) -> Result<Box<str>, ReadingError> {
self.get_str_bounded(i32::MAX as usize)
} }
#[inline]
fn get_uuid(&mut self) -> Result<uuid::Uuid, ReadingError> { fn get_uuid(&mut self) -> Result<uuid::Uuid, ReadingError> {
let mut bytes = [0u8; 16]; let mut bytes = [0u8; 16];
self.read_exact(&mut bytes) self.read_exact(&mut bytes)
@ -228,40 +361,31 @@ impl<R: Read> NetworkReadExt for R {
Ok(uuid::Uuid::from_bytes(bytes)) Ok(uuid::Uuid::from_bytes(bytes))
} }
#[inline]
fn get_fixed_bitset(&mut self, bits: usize) -> Result<FixedBitSet, ReadingError> { fn get_fixed_bitset(&mut self, bits: usize) -> Result<FixedBitSet, ReadingError> {
let bytes = self.read_boxed_slice(bits.div_ceil(8))?; let byte_count = bits.div_ceil(8);
Ok(bytes) let mut bytes = vec![0u8; byte_count];
self.read_bytes_to_buf(&mut bytes)?;
Ok(bytes.into_boxed_slice())
}
}
#[inline]
pub fn read_remaining_bytes(read: &mut impl Read, bound: usize) -> Result<Box<[u8]>, ReadingError> {
let mut return_buf = Vec::with_capacity(bound.min(1024));
// Take one extra byte to check for exceeding bound
read.take(bound as u64 + 1)
.read_to_end(&mut return_buf)
.map_err(|err| ReadingError::Incomplete(err.to_string()))?;
if return_buf.len() > bound {
return Err(ReadingError::TooLarge(
"Read remaining too long".to_string(),
));
} }
fn get_option<G>( Ok(return_buf.into_boxed_slice())
&mut self,
parse: impl FnOnce(&mut Self) -> Result<G, ReadingError>,
) -> Result<Option<G>, ReadingError> {
if self.get_bool()? {
Ok(Some(parse(self)?))
} else {
Ok(None)
}
}
fn get_list<G>(
&mut self,
parse: impl Fn(&mut Self) -> Result<G, ReadingError>,
) -> Result<Vec<G>, ReadingError> {
const MAX_LIST_SIZE: usize = 65536;
let len = self.get_var_int()?.0 as usize;
if len > MAX_LIST_SIZE {
return Err(ReadingError::TooLarge(format!(
"List length {len} exceeds limit"
)));
}
let mut list = Vec::with_capacity(len);
for _ in 0..len {
list.push(parse(self)?);
}
Ok(list)
}
} }
pub trait NetworkWriteExt { pub trait NetworkWriteExt {

View File

@ -240,3 +240,162 @@ impl<T: PacketRead> PacketRead for Option<T> {
bool::read(reader)?.then(|| T::read(reader)).transpose() bool::read(reader)?.then(|| T::read(reader)).transpose()
} }
} }
impl<'a> crate::serial::PacketReadSlice<'a> for bool {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.is_empty() {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected bool byte"));
}
let b = buf[0];
*buf = &buf[1..];
Ok(b != 0)
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for u8 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.is_empty() {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected u8"));
}
let b = buf[0];
*buf = &buf[1..];
Ok(b)
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for i8 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
u8::read_slice(buf).map(|b| b as Self)
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for i16 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.len() < 2 {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected i16"));
}
let (bytes, rest) = buf.split_at(2);
*buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap()))
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for i32 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.len() < 4 {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected i32"));
}
let (bytes, rest) = buf.split_at(4);
*buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap()))
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for i64 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.len() < 8 {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected i64"));
}
let (bytes, rest) = buf.split_at(8);
*buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap()))
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for u16 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.len() < 2 {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected u16"));
}
let (bytes, rest) = buf.split_at(2);
*buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap()))
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for u32 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.len() < 4 {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected u32"));
}
let (bytes, rest) = buf.split_at(4);
*buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap()))
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for u64 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.len() < 8 {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected u64"));
}
let (bytes, rest) = buf.split_at(8);
*buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap()))
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for f32 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.len() < 4 {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected f32"));
}
let (bytes, rest) = buf.split_at(4);
*buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap()))
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for f64 {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.len() < 8 {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected f64"));
}
let (bytes, rest) = buf.split_at(8);
*buf = rest;
Ok(Self::from_le_bytes(bytes.try_into().unwrap()))
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for &'a str {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
crate::serial::read_str_slice(buf)
}
}
impl<'a, T: crate::serial::PacketReadSlice<'a>> crate::serial::PacketReadSlice<'a> for Option<T> {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
bool::read_slice(buf)?
.then(|| T::read_slice(buf))
.transpose()
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for uuid::Uuid {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
if buf.len() < 16 {
return Err(Error::new(ErrorKind::UnexpectedEof, "expected Uuid"));
}
let (bytes, rest) = buf.split_at(16);
*buf = rest;
Ok(Self::from_bytes(bytes.try_into().unwrap()))
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for crate::codec::var_int::VarInt {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
Self::read(buf)
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for crate::codec::var_uint::VarUInt {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
Self::read(buf)
}
}
impl<'a> crate::serial::PacketReadSlice<'a> for crate::codec::var_ulong::VarULong {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error> {
Self::read(buf)
}
}

View File

@ -16,3 +16,32 @@ pub trait PacketRead: Sized {
Err(Error::other("Not implemented")) Err(Error::other("Not implemented"))
} }
} }
pub trait PacketReadSlice<'a>: Sized {
fn read_slice(buf: &mut &'a [u8]) -> Result<Self, Error>;
}
pub(crate) fn read_str_slice<'a>(buf: &mut &'a [u8]) -> Result<&'a str, Error> {
use crate::codec::var_uint::VarUInt;
use std::io::ErrorKind;
const MAX_STRING_LENGTH: usize = 32767;
let len = VarUInt::read(buf)?.0 as usize;
if len > MAX_STRING_LENGTH {
return Err(Error::new(
ErrorKind::InvalidData,
format!("String length {len} exceeds maximum of {MAX_STRING_LENGTH}"),
));
}
if len > buf.len() {
return Err(Error::new(
ErrorKind::UnexpectedEof,
"Not enough bytes for string",
));
}
let (str_bytes, rest) = buf.split_at(len);
*buf = rest;
std::str::from_utf8(str_bytes)
.map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid UTF-8 sequence"))
}

View File

@ -124,6 +124,13 @@ impl PacketWrite for String {
} }
} }
impl PacketWrite for str {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
VarUInt(self.len() as _).write(writer)?;
writer.write_all(self.as_bytes())
}
}
impl<T: PacketWrite> PacketWrite for Vector3<T> { impl<T: PacketWrite> PacketWrite for Vector3<T> {
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> { fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
self.x.write(writer)?; self.x.write(writer)?;

Some files were not shown because too many files have changed in this diff Show More