diff --git a/Cargo.lock b/Cargo.lock index 64a46ba7f..56d353f29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,13 +309,13 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.10.0-rc.6" +version = "0.10.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f895fb33c1ad22da4bc79d37c0bddff8aee2ba4575705345eb73b8ffbc386074" +checksum = "31cd65b2ca03198c223cd9a8fa1152c4ec251cd79049f6dc584152ad3fb5ba9d" dependencies = [ "cfg-if", "cpufeatures", - "rand_core 0.10.0-rc-3", + "rand_core 0.10.0-rc-5", ] [[package]] @@ -391,9 +391,9 @@ dependencies = [ [[package]] name = "cmov" -version = "0.5.0-pre.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417da527aa9bf6a1e10a781231effd1edd3ee82f27d5f8529ac9b279babce96" +checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3" [[package]] name = "colorchoice" @@ -658,13 +658,13 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.7.0-rc.18" +version = "0.7.0-rc.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37387ceb32048ff590f2cbd24d8b05fffe63c3f69a5cfa089d4f722ca4385a19" +checksum = "f9f9a78b88bb8255ec59a81423aa92ada22f96883f9ae59dcb68613907636ae5" dependencies = [ "ctutils", "num-traits", - "rand_core 0.10.0-rc-3", + "rand_core 0.10.0-rc-5", "serdect", "zeroize", ] @@ -681,29 +681,29 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.0-rc.9" +version = "0.2.0-rc.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b8986f836d4aeb30ccf4c9d3bd562fd716074cfd7fc4a2948359fbd21ed809" +checksum = "a6dcdb44f2c3ee25689ca12a4c19e664fd09f97aeae0bc5043b2dbab6389e308" dependencies = [ "hybrid-array", ] [[package]] name = "crypto-primes" -version = "0.7.0-pre.6" +version = "0.7.0-pre.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79c98a281f9441200b24e3151407a629bfbe720399186e50516da939195e482" +checksum = "c6372ba15f988d7cd77e9cfbc42b269601c006f2f16a21a72b886136caf04bfb" dependencies = [ - "crypto-bigint 0.7.0-rc.18", + "crypto-bigint 0.7.0-rc.21", "libm", - "rand_core 0.10.0-rc-3", + "rand_core 0.10.0-rc-5", ] [[package]] name = "ctutils" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758e5ed90be3c8abff7f9a6f37ab7f6d8c59c2210d448b81f3f508134aec84e4" +checksum = "1005a6d4446f5120ef475ad3d2af2b30c49c2c9c6904258e3bb30219bebed5e4" dependencies = [ "cmov", ] @@ -767,13 +767,13 @@ dependencies = [ [[package]] name = "digest" -version = "0.11.0-rc.5" +version = "0.11.0-rc.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf9423bafb058e4142194330c52273c343f8a5beb7176d052f0e73b17dd35b9" +checksum = "ca14c221bd9052fd2da7c34a2eeb5ae54732db28be47c35937be71793d675422" dependencies = [ "block-buffer 0.11.0", "const-oid 0.10.2", - "crypto-common 0.2.0-rc.9", + "crypto-common 0.2.0-rc.12", "subtle", ] @@ -1097,7 +1097,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "rand_core 0.10.0-rc-3", + "rand_core 0.10.0-rc-5", "wasip2", ] @@ -1208,7 +1208,7 @@ version = "0.13.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1c597ac7d6cc8143e30e83ef70915e7f883b18d8bec2e2b2bce47f5bbb06d57" dependencies = [ - "digest 0.11.0-rc.5", + "digest 0.11.0-rc.7", ] [[package]] @@ -1538,9 +1538,9 @@ checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libloading" -version = "0.8.9" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" dependencies = [ "cfg-if", "windows-link", @@ -1993,9 +1993,9 @@ dependencies = [ [[package]] name = "pkcs8" -version = "0.11.0-rc.8" +version = "0.11.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77089aec8290d0b7bb01b671b091095cf1937670725af4fd73d47249f03b12c0" +checksum = "80f8fa6196ede5a9f9ee95b44ca134bddc9b70e8913f9297bd58c909f5889a09" dependencies = [ "der 0.8.0-rc.10", "spki 0.8.0-rc.4", @@ -2117,7 +2117,7 @@ dependencies = [ "libloading", "log", "num-bigint", - "pkcs8 0.11.0-rc.8", + "pkcs8 0.11.0-rc.9", "pumpkin-config", "pumpkin-data", "pumpkin-inventory", @@ -2206,6 +2206,7 @@ version = "0.1.0-dev+1.21.11" dependencies = [ "proc-macro-error2", "proc-macro2", + "pumpkin-data", "quote", "syn", ] @@ -2336,12 +2337,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.0-rc.6" -source = "git+https://github.com/rust-random/rand#f356bbae5817f382b19ac5741b549c4d2cd49b50" +version = "0.10.0-rc.7" +source = "git+https://github.com/rust-random/rand#6e80534233d4bb09ed0abc086edeacaae6f54dc0" dependencies = [ "chacha20", "getrandom 0.4.0-rc.0", - "rand_core 0.10.0-rc-3", + "rand_core 0.10.0-rc-5", ] [[package]] @@ -2355,9 +2356,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.0-rc-3" +version = "0.10.0-rc-5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66ee92bc15280519ef199a274fe0cafff4245d31bc39aaa31c011ad56cb1f05" +checksum = "05a06e03bd1f2ae861ab9e7498b6c64ed3dadb9ce175c0464a2522a5f23c0045" [[package]] name = "rayon" @@ -2443,19 +2444,19 @@ dependencies = [ [[package]] name = "rsa" -version = "0.10.0-rc.12" +version = "0.10.0-rc.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a2b1eacbc34fbaf77f6f1db1385518446008d49b9f9f59dc9d1340fce4ca9e" +checksum = "8ff85dd219e338d42a2eada54ad71fe515717e08d53f728a8803de33b83f80b8" dependencies = [ "const-oid 0.10.2", - "crypto-bigint 0.7.0-rc.18", + "crypto-bigint 0.7.0-rc.21", "crypto-primes", - "digest 0.11.0-rc.5", + "digest 0.11.0-rc.7", "pkcs1", - "pkcs8 0.11.0-rc.8", - "rand_core 0.10.0-rc-3", + "pkcs8 0.11.0-rc.9", + "rand_core 0.10.0-rc-5", "sha1", - "signature 3.0.0-rc.6", + "signature 3.0.0-rc.8", "spki 0.8.0-rc.4", "zeroize", ] @@ -2661,7 +2662,7 @@ checksum = "aa1ae819b9870cadc959a052363de870944a1646932d274a4e270f64bf79e5ef" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.11.0-rc.5", + "digest 0.11.0-rc.7", ] [[package]] @@ -2683,7 +2684,7 @@ checksum = "19d43dc0354d88b791216bb5c1bfbb60c0814460cc653ae0ebd71f286d0bd927" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.11.0-rc.5", + "digest 0.11.0-rc.7", ] [[package]] @@ -2723,12 +2724,12 @@ dependencies = [ [[package]] name = "signature" -version = "3.0.0-rc.6" +version = "3.0.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a96996ccff7dfa16f052bd995b4cecc72af22c35138738dc029f0ead6608d" +checksum = "c04b70a14ee5f15e2e0c785a5fdb2e9a51138dfe13ba3cf8eab037a9e60b1879" dependencies = [ - "digest 0.11.0-rc.5", - "rand_core 0.10.0-rc-3", + "digest 0.11.0-rc.7", + "rand_core 0.10.0-rc-5", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 17abadb69..a952db3cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ cesu8 = "1.1" cfb8 = "0.8" colored = "3.1" console-subscriber = { version = "0.5.0", default-features = false } -crc-fast = "1.9.0" +crc-fast = "1.10.0" criterion = { version = "0.8", default-features = false } crossbeam-utils = "0.8.21" crossfire = "2.1.10" @@ -72,7 +72,7 @@ heck = "0.5" hmac = "=0.13.0-rc.3" indexmap = "2.13" itertools = "0.14.0" -libloading = "0.8" +libloading = "0.9" lru = "0.16.3" lz4-java-wrc = "0.2.0" md5 = "0.8" @@ -82,7 +82,7 @@ num-traits = "0.2" num_cpus = "1.17.0" p384 = "0.13.1" phf = "0.13.1" -pkcs8 = "=0.11.0-rc.8" +pkcs8 = "=0.11.0-rc.9" proc-macro2 = "1.0" pumpkin-config = { path = "pumpkin-config" } pumpkin-data = { path = "pumpkin-data" } @@ -94,7 +94,7 @@ pumpkin-util = { path = "pumpkin-util" } pumpkin-world = { path = "pumpkin-world" } quote = "1.0" rand = { git = "https://github.com/rust-random/rand"} -rsa = "=0.10.0-rc.12" +rsa = "=0.10.0-rc.13" rustc-hash = "2.1.1" rustyline = "17.0.2" ruzstd = "0.8.2" diff --git a/pumpkin-macros/Cargo.toml b/pumpkin-macros/Cargo.toml index 921a5b933..aa42a1adf 100644 --- a/pumpkin-macros/Cargo.toml +++ b/pumpkin-macros/Cargo.toml @@ -9,6 +9,8 @@ proc-macro = true [dependencies] proc-macro-error2.workspace = true +pumpkin-data.workspace = true + proc-macro2.workspace = true quote.workspace = true diff --git a/pumpkin-macros/src/lib.rs b/pumpkin-macros/src/lib.rs index c971f0e37..6f72d3e37 100644 --- a/pumpkin-macros/src/lib.rs +++ b/pumpkin-macros/src/lib.rs @@ -1,9 +1,11 @@ use proc_macro::TokenStream; use proc_macro_error2::{abort, abort_call_site, proc_macro_error}; +use pumpkin_data::Block; +use pumpkin_data::tag::{RegistryKey, get_tag_ids}; use quote::quote; use syn::spanned::Spanned; use syn::{self, Attribute, DeriveInput, LitStr, Type, parse_quote}; -use syn::{Block, Expr, Field, Fields, ItemStruct, Stmt, parse_macro_input}; +use syn::{Expr, Field, Fields, ItemStruct, Stmt, parse_macro_input}; #[proc_macro_derive(Event)] pub fn event(item: TokenStream) -> TokenStream { @@ -79,7 +81,7 @@ pub fn cancellable(_args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_error] #[proc_macro] pub fn send_cancellable(input: TokenStream) -> TokenStream { - let block = parse_macro_input!(input as Block); + let block = parse_macro_input!(input as syn::Block); let mut event_expr = None; let mut after_block = None; @@ -167,42 +169,44 @@ pub fn packet(args: TokenStream, item: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn pumpkin_block(args: TokenStream, item: TokenStream) -> TokenStream { + let input_item = item.clone(); + let arg_lit = parse_macro_input!(args as LitStr); let arg_value = arg_lit.value(); - let (namespace, id) = match arg_value.split_once(':') { - Some(pair) => pair, + let block_name = arg_value.strip_prefix("minecraft:").unwrap_or(&arg_value); + let block = match Block::from_name(block_name) { + Some(b) => b, None => { - return syn::Error::new( - arg_lit.span(), - "Expected format \"namespace:id\" (e.g. \"minecraft:stone\")", - ) - .to_compile_error() - .into(); + return syn::Error::new(arg_lit.span(), "Invalid block name") + .to_compile_error() + .into(); } }; + let block_id = block.id; let ast = parse_macro_input!(item as DeriveInput); let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - let code = quote! { - #ast + let generated = quote! { impl #impl_generics crate::block::BlockMetadata for #name #ty_generics #where_clause { - fn namespace(&self) -> &'static str { - #namespace - } - fn ids(&self) -> &'static [&'static str] { - &[#id] + fn ids() -> Box<[u16]> { + [#block_id].into() } } }; - code.into() + // Combine original item and new impl + let mut output = input_item; + output.extend(TokenStream::from(generated)); + output } #[proc_macro_attribute] pub fn pumpkin_block_from_tag(args: TokenStream, item: TokenStream) -> TokenStream { + let original_item = item.clone(); + let arg_lit = parse_macro_input!(args as LitStr); let ast = parse_macro_input!(item as DeriveInput); @@ -211,24 +215,29 @@ pub fn pumpkin_block_from_tag(args: TokenStream, item: TokenStream) -> TokenStre let full_tag = arg_lit.value(); - // Efficient splitting - let namespace = match full_tag.split_once(':') { - Some((ns, _)) => ns, - None => abort!(arg_lit.span(), "Expected format 'namespace:path'"), + let values = match get_tag_ids(RegistryKey::Block, &full_tag) { + Some(v) => v, + None => { + return syn::Error::new( + arg_lit.span(), + format!("Failed to get tag IDs: {}", full_tag), + ) + .to_compile_error() + .into(); + } }; - quote! { - #ast + let expanded = quote! { impl #impl_generics crate::block::BlockMetadata for #name #ty_generics #where_clause { - fn namespace(&self) -> &'static str { - #namespace - } - fn ids(&self) -> &'static [&'static str] { - get_tag_values(RegistryKey::Block, #arg_lit).unwrap() + fn ids() -> Box<[u16]> { + Box::new([ #(#values),* ]) } } - } - .into() + }; + + let mut output = original_item; + output.extend(TokenStream::from(expanded)); + output } // #[proc_macro_error] diff --git a/pumpkin-nbt/src/compound.rs b/pumpkin-nbt/src/compound.rs index 35252bc42..902d6ac97 100644 --- a/pumpkin-nbt/src/compound.rs +++ b/pumpkin-nbt/src/compound.rs @@ -8,6 +8,12 @@ use crate::{END_ID, Error, Nbt, get_nbt_string}; use std::io::{ErrorKind, Read, Seek, Write}; use std::vec::IntoIter; +/// Represents a Compound NBT tag, effectively a Key-Value map. +/// +/// Internally, this uses a `Vec<(String, NbtTag)>` to preserve insertion order, +/// which is often preferred in NBT serialization, though lookups are O(n). +/// +/// #[derive(Clone, Debug, Default, PartialEq, PartialOrd)] pub struct NbtCompound { pub child_tags: Vec<(String, NbtTag)>, @@ -24,20 +30,10 @@ impl NbtCompound { loop { let tag_id = match reader.get_u8_be() { Ok(id) => id, - Err(err) => match err { - Error::Incomplete(err) => match err.kind() { - ErrorKind::UnexpectedEof => { - break; - } - _ => { - return Err(Error::Incomplete(err)); - } - }, - _ => { - return Err(err); - } - }, + Err(Error::Incomplete(e)) if e.kind() == ErrorKind::UnexpectedEof => break, + Err(e) => return Err(e), }; + if tag_id == END_ID { break; } @@ -45,6 +41,7 @@ impl NbtCompound { let len = reader.get_u16_be()?; reader.skip_bytes(len as i64)?; + // Skip Value NbtTag::skip_data(reader, tag_id)?; } @@ -59,27 +56,18 @@ impl NbtCompound { loop { let tag_id = match reader.get_u8_be() { Ok(id) => id, - Err(err) => match err { - Error::Incomplete(err) => match err.kind() { - ErrorKind::UnexpectedEof => { - break; - } - _ => { - return Err(Error::Incomplete(err)); - } - }, - _ => { - return Err(err); - } - }, + Err(Error::Incomplete(e)) if e.kind() == ErrorKind::UnexpectedEof => break, + Err(e) => return Err(e), }; + if tag_id == END_ID { break; } let name = get_nbt_string(reader)?; let tag = NbtTag::deserialize_data(reader, tag_id)?; - compound.put(&name, tag); + + compound.child_tags.push((name, tag)); } Ok(compound) @@ -100,9 +88,8 @@ impl NbtCompound { } pub fn put(&mut self, name: &str, value: impl Into) { - let name = name.to_string(); - if !self.child_tags.iter().any(|(key, _)| key == &name) { - self.child_tags.push((name, value.into())); + if !self.child_tags.iter().any(|(key, _)| key == name) { + self.child_tags.push((name.to_string(), value.into())); } } @@ -292,7 +279,7 @@ impl Display for NbtCompound { if i > 0 { f.write_str(", ")?; } - f.write_str(&format!("{key}: {value}"))?; + write!(f, "{}: {}", key, value)?; } f.write_str("}") } @@ -302,51 +289,51 @@ impl Display for NbtTag { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { NbtTag::End => Ok(()), - NbtTag::Byte(value) => f.write_fmt(format_args!("{value}")), - NbtTag::Short(value) => f.write_fmt(format_args!("{value}")), - NbtTag::Int(value) => f.write_fmt(format_args!("{value}")), - NbtTag::Long(value) => f.write_fmt(format_args!("{value}")), - NbtTag::Float(value) => f.write_fmt(format_args!("{value}")), - NbtTag::Double(value) => f.write_fmt(format_args!("{value}")), - NbtTag::String(value) => f.write_fmt(format_args!("\"{value}\"")), - NbtTag::Compound(value) => f.write_fmt(format_args!("{value}")), - NbtTag::ByteArray(value) => { - f.write_str("[B; ")?; - for (i, byte) in value.iter().enumerate() { + NbtTag::Byte(v) => write!(f, "{v}b"), + NbtTag::Short(v) => write!(f, "{v}s"), + NbtTag::Int(v) => write!(f, "{v}"), + NbtTag::Long(v) => write!(f, "{v}L"), + NbtTag::Float(v) => write!(f, "{v}f"), + NbtTag::Double(v) => write!(f, "{v}d"), + NbtTag::String(v) => write!(f, "\"{v}\""), // TODO: Proper escaping needed for robust SNBT + NbtTag::Compound(v) => write!(f, "{v}"), + NbtTag::ByteArray(v) => { + f.write_str("[B;")?; + for (i, byte) in v.iter().enumerate() { if i > 0 { - f.write_str(", ")?; + f.write_str(",")? } - f.write_fmt(format_args!("{byte}"))?; + write!(f, " {}b", byte)?; } f.write_str("]") } - NbtTag::List(value) => { + NbtTag::List(v) => { f.write_str("[")?; - for (i, tag) in value.iter().enumerate() { + for (i, tag) in v.iter().enumerate() { if i > 0 { - f.write_str(", ")?; + f.write_str(", ")? } - f.write_fmt(format_args!("{tag}"))?; + write!(f, "{}", tag)?; } f.write_str("]") } - NbtTag::IntArray(value) => { - f.write_str("[I; ")?; - for (i, int) in value.iter().enumerate() { + NbtTag::IntArray(v) => { + f.write_str("[I;")?; + for (i, int) in v.iter().enumerate() { if i > 0 { - f.write_str(", ")?; + f.write_str(",")? } - f.write_fmt(format_args!("{int}"))?; + write!(f, " {}", int)?; } f.write_str("]") } - NbtTag::LongArray(value) => { - f.write_str("[L; ")?; - for (i, long) in value.iter().enumerate() { + NbtTag::LongArray(v) => { + f.write_str("[L;")?; + for (i, long) in v.iter().enumerate() { if i > 0 { - f.write_str(", ")?; + f.write_str(",")? } - f.write_fmt(format_args!("{long}"))?; + write!(f, " {}L", long)?; } f.write_str("]") } diff --git a/pumpkin-nbt/src/tag.rs b/pumpkin-nbt/src/tag.rs index 64e038e3c..22ba19a05 100644 --- a/pumpkin-nbt/src/tag.rs +++ b/pumpkin-nbt/src/tag.rs @@ -302,14 +302,13 @@ impl NbtTag { pub fn extract_bool(&self) -> Option { match self { - NbtTag::Byte(byte) => Some(*byte != 0), + NbtTag::Byte(byte) => Some(byte != &0), _ => None, } } pub fn extract_byte_array(&self) -> Option> { match self { - // Note: Bytes are free to clone, so we can hand out an owned type. NbtTag::ByteArray(byte_array) => Some(byte_array.clone()), _ => None, } diff --git a/pumpkin-protocol/src/bedrock/client/network_chunk_publisher_update.rs b/pumpkin-protocol/src/bedrock/client/network_chunk_publisher_update.rs index dffd4ccf6..36a0ac11b 100644 --- a/pumpkin-protocol/src/bedrock/client/network_chunk_publisher_update.rs +++ b/pumpkin-protocol/src/bedrock/client/network_chunk_publisher_update.rs @@ -4,14 +4,14 @@ use pumpkin_macros::packet; use pumpkin_util::math::{position::BlockPos, vector2::Vector2}; use crate::{ - codec::{var_int::VarInt, var_uint::VarUInt}, + codec::{bedrock_block_pos::NetworkPos, var_int::VarInt, var_uint::VarUInt}, serial::PacketWrite, }; #[packet(121)] pub struct CNetworkChunkPublisherUpdate { // https://mojang.github.io/bedrock-protocol-docs/html/NetworkChunkPublisherUpdatePacket.html - pub pos_for_view: BlockPos, + pub pos_for_view: NetworkPos, // Is in blocks, not chunks! pub new_radius: VarUInt, // TODO @@ -21,8 +21,8 @@ pub struct CNetworkChunkPublisherUpdate { impl CNetworkChunkPublisherUpdate { pub fn new(pos_for_view: BlockPos, new_radius: u32) -> Self { Self { - pos_for_view, - new_radius: VarUInt(new_radius << 4), + pos_for_view: NetworkPos(pos_for_view), + new_radius: VarUInt(new_radius), server_build_chunk_list: Vec::new(), } } @@ -30,7 +30,7 @@ impl CNetworkChunkPublisherUpdate { impl PacketWrite for CNetworkChunkPublisherUpdate { fn write(&self, writer: &mut W) -> Result<(), Error> { - self.pos_for_view.write(writer)?; + self.pos_for_view.write_signed(writer)?; self.new_radius.write(writer)?; (self.server_build_chunk_list.len() as u32).write(writer)?; diff --git a/pumpkin-protocol/src/bedrock/client/start_game.rs b/pumpkin-protocol/src/bedrock/client/start_game.rs index b771a1479..c68827772 100644 --- a/pumpkin-protocol/src/bedrock/client/start_game.rs +++ b/pumpkin-protocol/src/bedrock/client/start_game.rs @@ -35,7 +35,7 @@ pub struct CStartGame { pub current_level_time: u64, pub enchantment_seed: VarInt, pub block_properties_size: VarUInt, - //pub block_properties: [GG; 2], + pub multiplayer_correlation_id: String, pub enable_itemstack_net_manager: bool, pub server_version: String, diff --git a/pumpkin-util/src/jwt/mod.rs b/pumpkin-util/src/jwt/mod.rs index 3c00b7984..721d0dcaf 100644 --- a/pumpkin-util/src/jwt/mod.rs +++ b/pumpkin-util/src/jwt/mod.rs @@ -1,3 +1,212 @@ -pub mod verifier; +//! # JWT Verifier for Minecraft: Bedrock Edition +//! +//! This module provides the core functionality for verifying the chain of JWT tokens +//! sent by a Minecraft: Bedrock Edition client. It handles cryptographic signature +//! verification, public key extraction, and decoding of player data. -pub use verifier::*; +use base64::{Engine as _, engine::general_purpose}; +use ecdsa::Signature; +use p384::PublicKey; +use p384::ecdsa::{VerifyingKey, signature::Verifier}; +use p384::pkcs8::DecodePublicKey; +use serde::Deserialize; +use serde_json::Value; +use thiserror::Error; + +/// Represents the claims extracted from the player's JWT token. +/// +/// This struct contains the player's display name, UUID, and XUID. +#[derive(Debug, Deserialize)] +pub struct PlayerClaims { + /// The player's display name (in-game name). + #[serde(rename = "displayName")] + pub display_name: String, + /// The player's unique identifier (UUID). + #[serde(rename = "identity")] + pub uuid: String, + /// The player's Xbox User ID (XUID). + #[serde(rename = "XUID")] + pub xuid: String, +} + +/// Represents the possible errors that can occur during JWT verification. +#[derive(Debug, Error)] +pub enum AuthError { + /// Indicates that a JWT token has an invalid format (not enough parts). + #[error("Invalid token format")] + InvalidTokenFormat, + /// Indicates that the 'x5u' (X.509 URL) header parameter is missing from a token. + #[error("x5u not found in header")] + MissingX5U, + /// Indicates a failure in Base64 decoding. + #[error("Base64 decoding failed: {0}")] + Base64Decode(#[from] base64::DecodeError), + /// Indicates a failure in parsing JSON data. + #[error("JSON parse error: {0}")] + JsonParse(#[from] serde_json::Error), + /// Indicates a failure in building a public key from its representation. + #[error("Public key build failed: {0}")] + PublicKeyBuild(String), + /// Indicates that the token was not signed by the trusted Mojang public key. + #[error("Token not signed by trusted Mojang key")] + MojangKeyMismatch, + /// Indicates that the token's signature is invalid. + #[error("Invalid signature")] + InvalidSignature, + /// Indicates an error related to ECDSA signature operations. + #[error("ECDSA signature error: {0}")] + Ecdsa(#[from] ecdsa::Error), +} + +/// Decodes a Base64 URL-safe encoded string with no padding. +/// +/// # Arguments +/// +/// * `s` - The Base64 URL-safe encoded string to decode. +/// +/// # Returns +/// +/// A `Result` containing the decoded bytes or a `base64::DecodeError`. +pub fn decode_b64_url_nopad(s: &str) -> Result, base64::DecodeError> { + general_purpose::URL_SAFE_NO_PAD.decode(s) +} + +/// Decodes a standard Base64 encoded string. +/// +/// # Arguments +/// +/// * `s` - The standard Base64 encoded string to decode. +/// +/// # Returns +/// +/// A `Result` containing the decoded bytes or a `base64::DecodeError`. +pub fn decode_b64_standard(s: &str) -> Result, base64::DecodeError> { + general_purpose::STANDARD.decode(s) +} + +/// Builds a P-384 public key from a Base64 encoded string. +/// +/// This function supports several common public key formats. +/// +/// # Arguments +/// +/// * `b64` - The Base64 encoded public key. +/// +/// # Returns +/// +/// A `Result` containing the `p384::PublicKey` or an `AuthError`. +pub fn build_public_key_from_b64(b64: &str) -> Result { + let bytes = decode_b64_standard(b64)?; + + if !bytes.is_empty() && bytes[0] == 0x30 { + PublicKey::from_public_key_der(&bytes).map_err(|e| AuthError::PublicKeyBuild(e.to_string())) + } else if bytes.len() == 97 && bytes[0] == 0x04 { + PublicKey::from_sec1_bytes(&bytes).map_err(|e| AuthError::PublicKeyBuild(e.to_string())) + } else if bytes.len() == 96 { + let mut sec1 = Vec::with_capacity(97); + sec1.push(0x04u8); + sec1.extend_from_slice(&bytes); + PublicKey::from_sec1_bytes(&sec1).map_err(|e| AuthError::PublicKeyBuild(e.to_string())) + } else { + Err(AuthError::PublicKeyBuild(format!( + "Unsupported key format/length: {} bytes", + bytes.len() + ))) + } +} + +/// Decodes the header of a JWT and extracts the 'x5u' (X.509 URL) value. +/// +/// # Arguments +/// +/// * `header_b64` - The Base64 URL-safe encoded header of the JWT. +/// +/// # Returns +/// +/// A `Result` containing the 'x5u' value as a string or an `AuthError`. +pub fn decode_header_get_x5u(header_b64: &str) -> Result { + let header_bytes = decode_b64_url_nopad(header_b64)?; + let header_json: Value = serde_json::from_slice(&header_bytes)?; + if let Some(x5u) = header_json.get("x5u") + && let Some(s) = x5u.as_str() + { + return Ok(s.to_string()); + } + Err(AuthError::MissingX5U) +} + +/// Verifies a chain of JWT tokens from a Minecraft: Bedrock Edition client. +/// +/// This function performs the following steps: +/// 1. Decodes and verifies each token in the chain. +/// 2. Ensures that the chain is properly linked, with each token being signed by the key from the previous one. +/// 3. Verifies that the second token in the chain is signed by the trusted Mojang public key. +/// 4. Extracts and returns the player's claims from the final token in the chain. +/// +/// # Arguments +/// +/// * `raw_chain` - A slice of strings, where each string is a raw JWT token. +/// * `mojang_key_b64` - The Base64 encoded Mojang public key. +/// +/// # Returns +/// +/// A `Result` containing the `PlayerClaims` if verification is successful, or an `AuthError` if it fails. +pub fn verify_chain(raw_chain: &[&str], mojang_key_b64: &str) -> Result { + let mojang_pk = VerifyingKey::from(build_public_key_from_b64(mojang_key_b64)?); + + let tokens: Vec = raw_chain + .iter() + .map(|t| t.trim().replace(['\n', '\r'], "")) + .collect(); + + let first_token = tokens.first().ok_or(AuthError::InvalidTokenFormat)?; + let first_header_b64 = first_token + .split('.') + .next() + .ok_or(AuthError::InvalidTokenFormat)?; + + let mut current_verifying_key = VerifyingKey::from(build_public_key_from_b64( + &decode_header_get_x5u(first_header_b64)?, + )?); + + // 4. Verification Loop + for (i, token) in tokens.iter().enumerate() { + let (signing_input, b64_sig) = token + .rsplit_once('.') + .ok_or(AuthError::InvalidTokenFormat)?; + + let sig_bytes = decode_b64_url_nopad(b64_sig)?; + let signature = + Signature::from_slice(&sig_bytes).map_err(|_| AuthError::InvalidSignature)?; + + current_verifying_key + .verify(signing_input.as_bytes(), &signature) + .map_err(|_| AuthError::InvalidSignature)?; + + if i == 1 && current_verifying_key != mojang_pk { + return Err(AuthError::MojangKeyMismatch); + } + + let payload_b64 = signing_input + .split('.') + .nth(1) + .ok_or(AuthError::InvalidTokenFormat)?; + let payload_bytes = decode_b64_url_nopad(payload_b64)?; + let payload: Value = serde_json::from_slice(&payload_bytes)?; + + if let Some(next_key_b64) = payload.get("identityPublicKey").and_then(|v| v.as_str()) { + current_verifying_key = VerifyingKey::from(build_public_key_from_b64(next_key_b64)?); + } + } + + let last_token = tokens.last().ok_or(AuthError::InvalidTokenFormat)?; + let last_payload_b64 = last_token + .split('.') + .nth(1) + .ok_or(AuthError::InvalidTokenFormat)?; + let payload_bytes = decode_b64_url_nopad(last_payload_b64)?; + let v: Value = serde_json::from_slice(&payload_bytes)?; + let extra_data: PlayerClaims = serde_json::from_value(v["extraData"].clone())?; + + Ok(extra_data) +} diff --git a/pumpkin-util/src/jwt/verifier.rs b/pumpkin-util/src/jwt/verifier.rs deleted file mode 100644 index 5435186f5..000000000 --- a/pumpkin-util/src/jwt/verifier.rs +++ /dev/null @@ -1,276 +0,0 @@ -//! # JWT Verifier for Minecraft: Bedrock Edition -//! -//! This module provides the core functionality for verifying the chain of JWT tokens -//! sent by a Minecraft: Bedrock Edition client. It handles cryptographic signature -//! verification, public key extraction, and decoding of player data. - -use base64::{Engine as _, engine::general_purpose}; -use p384::PublicKey; -use p384::ecdsa::{Signature as EcdsaSignature, VerifyingKey, signature::Verifier}; -use p384::pkcs8::{DecodePublicKey, EncodePublicKey}; -use serde::Deserialize; -use serde_json::Value; -use thiserror::Error; - -/// Represents the claims extracted from the player's JWT token. -/// -/// This struct contains the player's display name, UUID, and XUID. -#[derive(Debug, Deserialize)] -pub struct PlayerClaims { - /// The player's display name (in-game name). - #[serde(rename = "displayName")] - pub display_name: String, - /// The player's unique identifier (UUID). - #[serde(rename = "identity")] - pub uuid: String, - /// The player's Xbox User ID (XUID). - #[serde(rename = "XUID")] - pub xuid: String, -} - -/// Represents the possible errors that can occur during JWT verification. -#[derive(Debug, Error)] -pub enum AuthError { - /// Indicates that a JWT token has an invalid format (not enough parts). - #[error("Invalid token format")] - InvalidTokenFormat, - /// Indicates that the 'x5u' (X.509 URL) header parameter is missing from a token. - #[error("x5u not found in header")] - MissingX5U, - /// Indicates a failure in Base64 decoding. - #[error("Base64 decoding failed: {0}")] - Base64Decode(#[from] base64::DecodeError), - /// Indicates a failure in parsing JSON data. - #[error("JSON parse error: {0}")] - JsonParse(#[from] serde_json::Error), - /// Indicates a failure in building a public key from its representation. - #[error("Public key build failed: {0}")] - PublicKeyBuild(String), - /// Indicates that the token was not signed by the trusted Mojang public key. - #[error("Token not signed by trusted Mojang key")] - MojangKeyMismatch, - /// Indicates that the token's signature is invalid. - #[error("Invalid signature")] - InvalidSignature, - /// Indicates an error related to ECDSA signature operations. - #[error("ECDSA signature error: {0}")] - Ecdsa(#[from] ecdsa::Error), -} - -/// Decodes a Base64 URL-safe encoded string with no padding. -/// -/// # Arguments -/// -/// * `s` - The Base64 URL-safe encoded string to decode. -/// -/// # Returns -/// -/// A `Result` containing the decoded bytes or a `base64::DecodeError`. -pub fn decode_b64_url_nopad(s: &str) -> Result, base64::DecodeError> { - general_purpose::URL_SAFE_NO_PAD.decode(s) -} - -/// Decodes a standard Base64 encoded string. -/// -/// # Arguments -/// -/// * `s` - The standard Base64 encoded string to decode. -/// -/// # Returns -/// -/// A `Result` containing the decoded bytes or a `base64::DecodeError`. -pub fn decode_b64_standard(s: &str) -> Result, base64::DecodeError> { - general_purpose::STANDARD.decode(s) -} - -/// Builds a P-384 public key from a Base64 encoded string. -/// -/// This function supports several common public key formats. -/// -/// # Arguments -/// -/// * `b64` - The Base64 encoded public key. -/// -/// # Returns -/// -/// A `Result` containing the `p384::PublicKey` or an `AuthError`. -pub fn build_public_key_from_b64(b64: &str) -> Result { - let bytes = decode_b64_standard(b64)?; - - if !bytes.is_empty() && bytes[0] == 0x30 { - PublicKey::from_public_key_der(&bytes).map_err(|e| AuthError::PublicKeyBuild(e.to_string())) - } else if bytes.len() == 97 && bytes[0] == 0x04 { - PublicKey::from_sec1_bytes(&bytes).map_err(|e| AuthError::PublicKeyBuild(e.to_string())) - } else if bytes.len() == 96 { - let mut sec1 = Vec::with_capacity(97); - sec1.push(0x04u8); - sec1.extend_from_slice(&bytes); - PublicKey::from_sec1_bytes(&sec1).map_err(|e| AuthError::PublicKeyBuild(e.to_string())) - } else { - Err(AuthError::PublicKeyBuild(format!( - "Unsupported key format/length: {} bytes", - bytes.len() - ))) - } -} - -/// Converts a JOSE (JWS) format signature to a DER-encoded signature. -/// -/// # Arguments -/// -/// * `jose_sig` - The signature in JOSE format. -/// -/// # Returns -/// -/// A `Result` containing the DER-encoded signature or an `AuthError`. -pub fn jose_sig_to_der(jose_sig: &[u8]) -> Result, AuthError> { - if !jose_sig.len().is_multiple_of(2) { - return Err(AuthError::InvalidSignature); - } - let n = jose_sig.len() / 2; - let r = &jose_sig[..n]; - let s = &jose_sig[n..]; - - fn encode_integer_be(bytes: &[u8]) -> (usize, Vec) { - let mut i = 0usize; - while i < bytes.len() && bytes[i] == 0 { - i += 1; - } - let mut v = bytes[i..].to_vec(); - if v.is_empty() { - v.push(0u8); - } - if v[0] & 0x80 != 0 { - let mut pref = Vec::with_capacity(v.len() + 1); - pref.push(0u8); - pref.extend_from_slice(&v); - (pref.len(), pref) - } else { - let len = v.len(); - (len, v) - } - } - - let (r_len, r_enc) = encode_integer_be(r); - let (s_len, s_enc) = encode_integer_be(s); - - let seq_len = 2 + r_len + 2 + s_len; - - let mut der = Vec::with_capacity(2 + seq_len); - - der.push(0x30); - der.push(seq_len as u8); - - der.push(0x02); - der.push(r_len as u8); - der.extend_from_slice(&r_enc); - - der.push(0x02); - der.push(s_len as u8); - der.extend_from_slice(&s_enc); - - Ok(der) -} - -/// Decodes the header of a JWT and extracts the 'x5u' (X.509 URL) value. -/// -/// # Arguments -/// -/// * `header_b64` - The Base64 URL-safe encoded header of the JWT. -/// -/// # Returns -/// -/// A `Result` containing the 'x5u' value as a string or an `AuthError`. -pub fn decode_header_get_x5u(header_b64: &str) -> Result { - let header_bytes = decode_b64_url_nopad(header_b64)?; - let header_json: Value = serde_json::from_slice(&header_bytes)?; - if let Some(x5u) = header_json.get("x5u") - && let Some(s) = x5u.as_str() - { - return Ok(s.to_string()); - } - Err(AuthError::MissingX5U) -} - -/// Verifies a chain of JWT tokens from a Minecraft: Bedrock Edition client. -/// -/// This function performs the following steps: -/// 1. Decodes and verifies each token in the chain. -/// 2. Ensures that the chain is properly linked, with each token being signed by the key from the previous one. -/// 3. Verifies that the second token in the chain is signed by the trusted Mojang public key. -/// 4. Extracts and returns the player's claims from the final token in the chain. -/// -/// # Arguments -/// -/// * `raw_chain` - A slice of strings, where each string is a raw JWT token. -/// * `mojang_key_b64` - The Base64 encoded Mojang public key. -/// -/// # Returns -/// -/// A `Result` containing the `PlayerClaims` if verification is successful, or an `AuthError` if it fails. -pub fn verify_chain(raw_chain: &[&str], mojang_key_b64: &str) -> Result { - let tokens: Vec = raw_chain - .iter() - .map(|t| t.replace(['\n', '\r'], "")) - .collect(); - - let first_parts: Vec<&str> = tokens[0].split('.').collect(); - if first_parts.len() != 3 { - return Err(AuthError::InvalidTokenFormat); - } - let mut next_public_b64 = decode_header_get_x5u(first_parts[0])?; - - let mojang_pk = build_public_key_from_b64(mojang_key_b64)?; - - for (i, token) in tokens.iter().enumerate() { - let current_pub = build_public_key_from_b64(&next_public_b64)?; - - if i == 1 { - let cur_der = current_pub - .to_public_key_der() - .map_err(|e| AuthError::PublicKeyBuild(e.to_string()))?; - let moj_der = mojang_pk - .to_public_key_der() - .map_err(|e| AuthError::PublicKeyBuild(e.to_string()))?; - if cur_der.as_ref() != moj_der.as_ref() { - return Err(AuthError::MojangKeyMismatch); - } - } - - let parts: Vec<&str> = token.split('.').collect(); - if parts.len() != 3 { - return Err(AuthError::InvalidTokenFormat); - } - let signing_input = format!("{}.{}", parts[0], parts[1]); - let signing_input_bytes = signing_input.as_bytes(); - - let sig_bytes = decode_b64_url_nopad(parts[2])?; - let der_sig = jose_sig_to_der(&sig_bytes)?; - - let verifying_key = VerifyingKey::from(¤t_pub); - let signature = EcdsaSignature::from_der(&der_sig)?; - - if verifying_key - .verify(signing_input_bytes, &signature) - .is_err() - { - return Err(AuthError::InvalidSignature); - } - - let payload_bytes = decode_b64_url_nopad(parts[1])?; - let payload_json: Value = serde_json::from_slice(&payload_bytes)?; - if let Some(id_pk) = payload_json.get("identityPublicKey") - && let Some(s) = id_pk.as_str() - { - next_public_b64 = s.to_string(); - } - } - - let final_token = &tokens[tokens.len() - 1]; - let parts: Vec<&str> = final_token.split('.').collect(); - let payload = decode_b64_url_nopad(parts[1])?; - let v: Value = serde_json::from_slice(&payload)?; - let extra_data: PlayerClaims = serde_json::from_value(v["extraData"].clone())?; - - Ok(extra_data) -} diff --git a/pumpkin/src/block/blocks/anvil.rs b/pumpkin/src/block/blocks/anvil.rs index f52507cbd..de6c7532c 100644 --- a/pumpkin/src/block/blocks/anvil.rs +++ b/pumpkin/src/block/blocks/anvil.rs @@ -4,7 +4,6 @@ use crate::block::{ PlacedArgs, }; use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties}; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_world::BlockStateId; diff --git a/pumpkin/src/block/blocks/banners.rs b/pumpkin/src/block/blocks/banners.rs index ede7049de..befb1ac26 100644 --- a/pumpkin/src/block/blocks/banners.rs +++ b/pumpkin/src/block/blocks/banners.rs @@ -3,7 +3,6 @@ use crate::block::{ }; use crate::entity::EntityBase; use pumpkin_data::block_properties::{BlockProperties, WhiteBannerLikeProperties}; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; diff --git a/pumpkin/src/block/blocks/bed.rs b/pumpkin/src/block/blocks/bed.rs index df012cb41..ff843b711 100644 --- a/pumpkin/src/block/blocks/bed.rs +++ b/pumpkin/src/block/blocks/bed.rs @@ -5,7 +5,6 @@ use pumpkin_data::block_properties::BedPart; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::dimension::Dimension; use pumpkin_data::entity::EntityType; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::GameMode; use pumpkin_util::math::position::BlockPos; diff --git a/pumpkin/src/block/blocks/campfire.rs b/pumpkin/src/block/blocks/campfire.rs index 727841843..75c75370d 100644 --- a/pumpkin/src/block/blocks/campfire.rs +++ b/pumpkin/src/block/blocks/campfire.rs @@ -17,12 +17,8 @@ use crate::{ pub struct CampfireBlock; impl BlockMetadata for CampfireBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::CAMPFIRE.name, Block::SOUL_CAMPFIRE.name] + fn ids() -> Box<[u16]> { + [Block::CAMPFIRE.id, Block::SOUL_CAMPFIRE.id].into() } } diff --git a/pumpkin/src/block/blocks/candle_cakes.rs b/pumpkin/src/block/blocks/candle_cakes.rs index de2d8b0aa..e5162add9 100644 --- a/pumpkin/src/block/blocks/candle_cakes.rs +++ b/pumpkin/src/block/blocks/candle_cakes.rs @@ -1,10 +1,6 @@ use std::sync::Arc; -use pumpkin_data::{ - Block, - item::Item, - tag::{RegistryKey, get_tag_values}, -}; +use pumpkin_data::{Block, item::Item}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::{GameMode, math::position::BlockPos}; use pumpkin_world::{ diff --git a/pumpkin/src/block/blocks/candles.rs b/pumpkin/src/block/blocks/candles.rs index d2c6bf75c..0d05b54b9 100644 --- a/pumpkin/src/block/blocks/candles.rs +++ b/pumpkin/src/block/blocks/candles.rs @@ -3,7 +3,6 @@ use pumpkin_data::{ BlockDirection, block_properties::{BlockProperties, CandleLikeProperties, EnumVariants, Integer1To4}, entity::EntityPose, - tag::{RegistryKey, get_tag_values}, }; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; diff --git a/pumpkin/src/block/blocks/carpet.rs b/pumpkin/src/block/blocks/carpet.rs index b72ee1b24..be0d3a0a3 100644 --- a/pumpkin/src/block/blocks/carpet.rs +++ b/pumpkin/src/block/blocks/carpet.rs @@ -2,7 +2,6 @@ use crate::block::{ BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnScheduledTickArgs, }; use pumpkin_data::block_properties::is_air; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::{pumpkin_block, pumpkin_block_from_tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; diff --git a/pumpkin/src/block/blocks/carved_pumpkin.rs b/pumpkin/src/block/blocks/carved_pumpkin.rs index 96e57be37..f93bd9dae 100644 --- a/pumpkin/src/block/blocks/carved_pumpkin.rs +++ b/pumpkin/src/block/blocks/carved_pumpkin.rs @@ -17,12 +17,8 @@ use crate::{ pub struct CarvedPumpkinBlock; impl BlockMetadata for CarvedPumpkinBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::JACK_O_LANTERN.name, Block::CARVED_PUMPKIN.name] + fn ids() -> Box<[u16]> { + [Block::JACK_O_LANTERN.id, Block::CARVED_PUMPKIN.id].into() } } diff --git a/pumpkin/src/block/blocks/chests.rs b/pumpkin/src/block/blocks/chests.rs index 1aa8763f7..53f3438d5 100644 --- a/pumpkin/src/block/blocks/chests.rs +++ b/pumpkin/src/block/blocks/chests.rs @@ -5,7 +5,7 @@ use pumpkin_data::block_properties::{ BlockProperties, ChestLikeProperties, ChestType, HorizontalFacing, }; use pumpkin_data::entity::EntityPose; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; +use pumpkin_data::tag::{self}; use pumpkin_data::{Block, BlockDirection}; use pumpkin_inventory::double::DoubleInventory; use pumpkin_inventory::generic_container_screen_handler::{create_generic_9x3, create_generic_9x6}; @@ -67,12 +67,8 @@ impl ScreenHandlerFactory for ChestScreenFactory { pub struct ChestBlock; impl BlockMetadata for ChestBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - get_tag_values(RegistryKey::Block, "c:chests/wooden").unwrap() + fn ids() -> Box<[u16]> { + tag::Block::C_CHESTS_WOODEN.1.into() } } diff --git a/pumpkin/src/block/blocks/command.rs b/pumpkin/src/block/blocks/command.rs index fc7ae017e..e09614f2e 100644 --- a/pumpkin/src/block/blocks/command.rs +++ b/pumpkin/src/block/blocks/command.rs @@ -210,16 +210,13 @@ impl CommandBlock { } impl BlockMetadata for CommandBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[ - Block::COMMAND_BLOCK.name, - Block::CHAIN_COMMAND_BLOCK.name, - Block::REPEATING_COMMAND_BLOCK.name, + fn ids() -> Box<[u16]> { + [ + Block::COMMAND_BLOCK.id, + Block::CHAIN_COMMAND_BLOCK.id, + Block::REPEATING_COMMAND_BLOCK.id, ] + .into() } } diff --git a/pumpkin/src/block/blocks/doors.rs b/pumpkin/src/block/blocks/doors.rs index 8a1b946b6..e513b9d39 100644 --- a/pumpkin/src/block/blocks/doors.rs +++ b/pumpkin/src/block/blocks/doors.rs @@ -7,9 +7,7 @@ use pumpkin_data::block_properties::DoubleBlockHalf; use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::sound::Sound; use pumpkin_data::sound::SoundCategory; -use pumpkin_data::tag::RegistryKey; use pumpkin_data::tag::Taggable; -use pumpkin_data::tag::get_tag_values; use pumpkin_data::{Block, tag}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; diff --git a/pumpkin/src/block/blocks/falling.rs b/pumpkin/src/block/blocks/falling.rs index f51b8a722..a21bf0208 100644 --- a/pumpkin/src/block/blocks/falling.rs +++ b/pumpkin/src/block/blocks/falling.rs @@ -23,12 +23,8 @@ impl FallingBlock { } impl BlockMetadata for FallingBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::GRAVEL.name, Block::SAND.name, Block::RED_SAND.name] + fn ids() -> Box<[u16]> { + [Block::GRAVEL.id, Block::SAND.id, Block::RED_SAND.id].into() } } diff --git a/pumpkin/src/block/blocks/fence_gates.rs b/pumpkin/src/block/blocks/fence_gates.rs index 960f0967e..73e097a35 100644 --- a/pumpkin/src/block/blocks/fence_gates.rs +++ b/pumpkin/src/block/blocks/fence_gates.rs @@ -7,9 +7,7 @@ use crate::block::OnPlaceArgs; use crate::entity::player::Player; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::tag; -use pumpkin_data::tag::RegistryKey; use pumpkin_data::tag::Taggable; -use pumpkin_data::tag::get_tag_values; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; diff --git a/pumpkin/src/block/blocks/fences.rs b/pumpkin/src/block/blocks/fences.rs index fed466fc3..df36614b0 100644 --- a/pumpkin/src/block/blocks/fences.rs +++ b/pumpkin/src/block/blocks/fences.rs @@ -4,9 +4,7 @@ use crate::block::OnPlaceArgs; use pumpkin_data::BlockDirection; use pumpkin_data::BlockState; use pumpkin_data::block_properties::BlockProperties; -use pumpkin_data::tag::RegistryKey; use pumpkin_data::tag::Taggable; -use pumpkin_data::tag::get_tag_values; use pumpkin_data::{Block, tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; @@ -19,12 +17,8 @@ use crate::world::World; pub struct FenceBlock; impl BlockMetadata for FenceBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - get_tag_values(RegistryKey::Block, "c:fences").unwrap() + fn ids() -> Box<[u16]> { + tag::Block::C_FENCES.1.into() } } diff --git a/pumpkin/src/block/blocks/flower_pots.rs b/pumpkin/src/block/blocks/flower_pots.rs index b481f3f8d..3e8206ae3 100644 --- a/pumpkin/src/block/blocks/flower_pots.rs +++ b/pumpkin/src/block/blocks/flower_pots.rs @@ -3,7 +3,6 @@ use crate::block::{BlockBehaviour, BlockFuture, RandomTickArgs, UseWithItemArgs} use pumpkin_data::Block; use pumpkin_data::dimension::Dimension; use pumpkin_data::flower_pot_transformations::get_potted_item; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_world::world::BlockFlags; diff --git a/pumpkin/src/block/blocks/glass_panes.rs b/pumpkin/src/block/blocks/glass_panes.rs index eaefba45a..1b9d7ffe8 100644 --- a/pumpkin/src/block/blocks/glass_panes.rs +++ b/pumpkin/src/block/blocks/glass_panes.rs @@ -3,9 +3,7 @@ use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::BlockProperties; -use pumpkin_data::tag::RegistryKey; use pumpkin_data::tag::Taggable; -use pumpkin_data::tag::get_tag_values; use pumpkin_data::{Block, tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; @@ -17,12 +15,8 @@ use crate::world::World; pub struct GlassPaneBlock; impl BlockMetadata for GlassPaneBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - get_tag_values(RegistryKey::Block, "c:glass_panes").unwrap() + fn ids() -> Box<[u16]> { + tag::Block::C_GLASS_PANES.1.into() } } diff --git a/pumpkin/src/block/blocks/glazed_terracotta.rs b/pumpkin/src/block/blocks/glazed_terracotta.rs index c27de8c70..d5dc44a8b 100644 --- a/pumpkin/src/block/blocks/glazed_terracotta.rs +++ b/pumpkin/src/block/blocks/glazed_terracotta.rs @@ -1,16 +1,12 @@ use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnPlaceArgs}; use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties}; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; +use pumpkin_data::tag::{self}; use pumpkin_world::BlockStateId; pub struct GlazedTerracottaBlock; impl BlockMetadata for GlazedTerracottaBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - get_tag_values(RegistryKey::Block, "c:glazed_terracottas").unwrap() + fn ids() -> Box<[u16]> { + tag::Item::C_GLAZED_TERRACOTTAS.1.into() } } diff --git a/pumpkin/src/block/blocks/lanterns.rs b/pumpkin/src/block/blocks/lanterns.rs index ebf6a95ca..61cda0ff7 100644 --- a/pumpkin/src/block/blocks/lanterns.rs +++ b/pumpkin/src/block/blocks/lanterns.rs @@ -14,12 +14,8 @@ use pumpkin_world::world::BlockFlags; pub struct LanternBlock; impl BlockMetadata for LanternBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::LANTERN.name, Block::SOUL_LANTERN.name] + fn ids() -> Box<[u16]> { + [Block::LANTERN.id, Block::SOUL_LANTERN.id].into() } } diff --git a/pumpkin/src/block/blocks/logs.rs b/pumpkin/src/block/blocks/logs.rs index 794e2b0d8..e0a6b7335 100644 --- a/pumpkin/src/block/blocks/logs.rs +++ b/pumpkin/src/block/blocks/logs.rs @@ -1,5 +1,4 @@ use pumpkin_data::block_properties::BlockProperties; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_world::BlockStateId; diff --git a/pumpkin/src/block/blocks/piston/piston.rs b/pumpkin/src/block/blocks/piston/piston.rs index 524d116d5..d99a75176 100644 --- a/pumpkin/src/block/blocks/piston/piston.rs +++ b/pumpkin/src/block/blocks/piston/piston.rs @@ -29,12 +29,8 @@ pub(crate) type PistonProps = pumpkin_data::block_properties::StickyPistonLikePr pub struct PistonBlock; impl BlockMetadata for PistonBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::PISTON.name, Block::STICKY_PISTON.name] + fn ids() -> Box<[u16]> { + [Block::PISTON.id, Block::STICKY_PISTON.id].into() } } diff --git a/pumpkin/src/block/blocks/plant/bush.rs b/pumpkin/src/block/blocks/plant/bush.rs index 3f681a289..8f10223f5 100644 --- a/pumpkin/src/block/blocks/plant/bush.rs +++ b/pumpkin/src/block/blocks/plant/bush.rs @@ -9,12 +9,8 @@ use crate::block::{ pub struct BushBlock; impl BlockMetadata for BushBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::BUSH.name, Block::FIREFLY_BUSH.name] + fn ids() -> Box<[u16]> { + [Block::BUSH.id, Block::FIREFLY_BUSH.id].into() } } diff --git a/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs b/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs index 0db90ea3c..613cee6d4 100644 --- a/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs +++ b/pumpkin/src/block/blocks/plant/crop/gourds/attached_stem.rs @@ -18,15 +18,12 @@ type StemProperties = WheatLikeProperties; pub struct AttachedStemBlock; impl BlockMetadata for AttachedStemBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[ - Block::ATTACHED_PUMPKIN_STEM.name, - Block::ATTACHED_MELON_STEM.name, + fn ids() -> Box<[u16]> { + [ + Block::ATTACHED_PUMPKIN_STEM.id, + Block::ATTACHED_MELON_STEM.id, ] + .into() } } diff --git a/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs b/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs index 520278e28..139c827d7 100644 --- a/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs +++ b/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs @@ -27,12 +27,8 @@ type AttachedStemProperties = WallTorchLikeProperties; pub struct StemBlock; impl BlockMetadata for StemBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::PUMPKIN_STEM.name, Block::MELON_STEM.name] + fn ids() -> Box<[u16]> { + [Block::PUMPKIN_STEM.id, Block::MELON_STEM.id].into() } } diff --git a/pumpkin/src/block/blocks/plant/dry_vegetation.rs b/pumpkin/src/block/blocks/plant/dry_vegetation.rs index 3a762ad4a..4321e047d 100644 --- a/pumpkin/src/block/blocks/plant/dry_vegetation.rs +++ b/pumpkin/src/block/blocks/plant/dry_vegetation.rs @@ -1,5 +1,5 @@ -use pumpkin_data::tag; use pumpkin_data::tag::Taggable; +use pumpkin_data::{Block, tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockStateId, world::BlockAccessor}; @@ -11,12 +11,13 @@ use crate::block::{ pub struct DryVegetationBlock; impl BlockMetadata for DryVegetationBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &["dead_bush", "tall_dry_grass", "short_dry_grass"] + fn ids() -> Box<[u16]> { + [ + Block::DEAD_BUSH.id, + Block::TALL_DRY_GRASS.id, + Block::SHORT_DRY_GRASS.id, + ] + .into() } } diff --git a/pumpkin/src/block/blocks/plant/flower.rs b/pumpkin/src/block/blocks/plant/flower.rs index 384b86ae1..46c158358 100644 --- a/pumpkin/src/block/blocks/plant/flower.rs +++ b/pumpkin/src/block/blocks/plant/flower.rs @@ -1,6 +1,5 @@ -use pumpkin_data::Block; use pumpkin_data::dimension::Dimension; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; +use pumpkin_data::{Block, tag}; use pumpkin_world::BlockStateId; use pumpkin_world::world::BlockFlags; @@ -14,12 +13,8 @@ use crate::block::RandomTickArgs; pub struct FlowerBlock; impl BlockMetadata for FlowerBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - get_tag_values(RegistryKey::Block, "c:flowers/small").unwrap() + fn ids() -> Box<[u16]> { + tag::Block::C_FLOWERS_SMALL.1.into() } } diff --git a/pumpkin/src/block/blocks/plant/flowerbed.rs b/pumpkin/src/block/blocks/plant/flowerbed.rs index 80f684091..d3c7c2c83 100644 --- a/pumpkin/src/block/blocks/plant/flowerbed.rs +++ b/pumpkin/src/block/blocks/plant/flowerbed.rs @@ -16,12 +16,8 @@ type FlowerbedProperties = pumpkin_data::block_properties::PinkPetalsLikePropert pub struct FlowerbedBlock; impl BlockMetadata for FlowerbedBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &["pink_petals", "wildflowers"] + fn ids() -> Box<[u16]> { + [Block::PINK_PETALS.id, Block::WILDFLOWERS.id].into() } } diff --git a/pumpkin/src/block/blocks/plant/leaf_litter.rs b/pumpkin/src/block/blocks/plant/leaf_litter.rs index fce4b6f98..de7807970 100644 --- a/pumpkin/src/block/blocks/plant/leaf_litter.rs +++ b/pumpkin/src/block/blocks/plant/leaf_litter.rs @@ -13,12 +13,8 @@ type LeafLitterProperties = pumpkin_data::block_properties::LeafLitterLikeProper pub struct LeafLitterBlock; impl BlockMetadata for LeafLitterBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &["leaf_litter"] + fn ids() -> Box<[u16]> { + [Block::LEAF_LITTER.id].into() } } diff --git a/pumpkin/src/block/blocks/plant/mushroom_plant.rs b/pumpkin/src/block/blocks/plant/mushroom_plant.rs index 2175d26c0..a1f121bd6 100644 --- a/pumpkin/src/block/blocks/plant/mushroom_plant.rs +++ b/pumpkin/src/block/blocks/plant/mushroom_plant.rs @@ -1,5 +1,5 @@ -use pumpkin_data::tag; use pumpkin_data::tag::Taggable; +use pumpkin_data::{Block, tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockStateId, world::BlockAccessor}; @@ -11,12 +11,8 @@ use crate::block::{ pub struct MushroomPlantBlock; impl BlockMetadata for MushroomPlantBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &["brown_mushroom", "red_mushroom"] + fn ids() -> Box<[u16]> { + [Block::BROWN_MUSHROOM.id, Block::RED_MUSHROOM.id].into() } } diff --git a/pumpkin/src/block/blocks/plant/roots.rs b/pumpkin/src/block/blocks/plant/roots.rs index 7ec4f8b97..53fa69478 100644 --- a/pumpkin/src/block/blocks/plant/roots.rs +++ b/pumpkin/src/block/blocks/plant/roots.rs @@ -12,12 +12,8 @@ use crate::block::{ pub struct RootsBlock; impl BlockMetadata for RootsBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::WARPED_ROOTS.name, Block::CRIMSON_ROOTS.name] + fn ids() -> Box<[u16]> { + [Block::WARPED_ROOTS.id, Block::CRIMSON_ROOTS.id].into() } } diff --git a/pumpkin/src/block/blocks/plant/sapling.rs b/pumpkin/src/block/blocks/plant/sapling.rs index e1f40b3c2..d50e08852 100644 --- a/pumpkin/src/block/blocks/plant/sapling.rs +++ b/pumpkin/src/block/blocks/plant/sapling.rs @@ -1,5 +1,4 @@ use pumpkin_data::block_properties::{BlockProperties, Integer0To1}; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; diff --git a/pumpkin/src/block/blocks/plant/short_plant.rs b/pumpkin/src/block/blocks/plant/short_plant.rs index 83d0b2a07..eaa0a4345 100644 --- a/pumpkin/src/block/blocks/plant/short_plant.rs +++ b/pumpkin/src/block/blocks/plant/short_plant.rs @@ -1,3 +1,4 @@ +use pumpkin_data::Block; use pumpkin_world::BlockStateId; use crate::block::{ @@ -8,12 +9,8 @@ use crate::block::{ pub struct ShortPlantBlock; impl BlockMetadata for ShortPlantBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &["short_grass", "fern"] + fn ids() -> Box<[u16]> { + [Block::SHORT_GRASS.id, Block::FERN.id].into() } } diff --git a/pumpkin/src/block/blocks/plant/tall_plant.rs b/pumpkin/src/block/blocks/plant/tall_plant.rs index f5aca8a3c..13ef10b55 100644 --- a/pumpkin/src/block/blocks/plant/tall_plant.rs +++ b/pumpkin/src/block/blocks/plant/tall_plant.rs @@ -13,21 +13,18 @@ use crate::block::{ pub struct TallPlantBlock; impl BlockMetadata for TallPlantBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[ - "tall_grass", - "large_fern", - "pitcher_plant", + fn ids() -> Box<[u16]> { + [ + Block::TALL_GRASS.id, + Block::LARGE_FERN.id, + Block::PITCHER_PLANT.id, // TallFlowerBlocks - "sunflower", - "lilac", - "peony", - "rose_bush", + Block::SUNFLOWER.id, + Block::LILAC.id, + Block::PEONY.id, + Block::ROSE_BUSH.id, ] + .into() } } @@ -56,7 +53,7 @@ impl BlockBehaviour for TallPlantBlock { }; let (other_block, other_state_id) = args.world.get_block_and_state_id(&other_block_pos).await; - if self.ids().contains(&other_block.name) { + if Self::ids().contains(&other_block.id) { let other_props = TallSeagrassLikeProperties::from_state_id(other_state_id, other_block); let opposite_half = match tall_plant_props.half { diff --git a/pumpkin/src/block/blocks/redstone/buttons.rs b/pumpkin/src/block/blocks/redstone/buttons.rs index b4706c5f1..134692630 100644 --- a/pumpkin/src/block/blocks/redstone/buttons.rs +++ b/pumpkin/src/block/blocks/redstone/buttons.rs @@ -5,8 +5,6 @@ use pumpkin_data::BlockDirection; use pumpkin_data::HorizontalFacingExt; use pumpkin_data::block_properties::BlockFace; use pumpkin_data::block_properties::BlockProperties; -use pumpkin_data::tag::RegistryKey; -use pumpkin_data::tag::get_tag_values; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; diff --git a/pumpkin/src/block/blocks/redstone/copper_bulb.rs b/pumpkin/src/block/blocks/redstone/copper_bulb.rs index 994a27a0b..5f6f7f493 100644 --- a/pumpkin/src/block/blocks/redstone/copper_bulb.rs +++ b/pumpkin/src/block/blocks/redstone/copper_bulb.rs @@ -1,5 +1,6 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs}; +use pumpkin_data::Block; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_world::BlockStateId; @@ -10,21 +11,18 @@ type CopperBulbLikeProperties = pumpkin_data::block_properties::CopperBulbLikePr pub struct CopperBulbBlock; impl BlockMetadata for CopperBulbBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[ - "copper_bulb", - "exposed_copper_bulb", - "weathered_copper_bulb", - "oxidized_copper_bulb", - "waxed_copper_bulb", - "waxed_exposed_copper_bulb", - "waxed_weathered_copper_bulb", - "waxed_oxidized_copper_bulb", + fn ids() -> Box<[u16]> { + [ + Block::COPPER_BULB.id, + Block::EXPOSED_COPPER_BULB.id, + Block::WEATHERED_COPPER_BULB.id, + Block::OXIDIZED_COPPER_BULB.id, + Block::WAXED_COPPER_BULB.id, + Block::WAXED_EXPOSED_COPPER_BULB.id, + Block::WAXED_WEATHERED_COPPER_BULB.id, + Block::WAXED_OXIDIZED_COPPER_BULB.id, ] + .into() } } diff --git a/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs b/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs index bd307574f..7ad73fb2b 100644 --- a/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs +++ b/pumpkin/src/block/blocks/redstone/pressure_plate/plate.rs @@ -1,7 +1,7 @@ use pumpkin_data::{ Block, BlockDirection, BlockState, block_properties::BlockProperties, - tag::{RegistryKey, get_tag_values}, + tag::{self}, }; use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos}; use pumpkin_world::{BlockStateId, world::BlockFlags}; @@ -23,19 +23,11 @@ pub struct PressurePlateBlock; type PressurePlateProps = pumpkin_data::block_properties::StonePressurePlateLikeProperties; impl BlockMetadata for PressurePlateBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { + fn ids() -> Box<[u16]> { let mut combined = Vec::new(); - combined.extend_from_slice( - get_tag_values(RegistryKey::Block, "minecraft:wooden_pressure_plates").unwrap(), - ); - combined.extend_from_slice( - get_tag_values(RegistryKey::Block, "minecraft:stone_pressure_plates").unwrap(), - ); - combined.leak() + combined.extend_from_slice(tag::Block::MINECRAFT_WOODEN_PRESSURE_PLATES.1); + combined.extend_from_slice(tag::Block::MINECRAFT_STONE_PRESSURE_PLATES.1); + combined.into_boxed_slice() } } diff --git a/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs b/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs index 4487a6fe5..61fb12042 100644 --- a/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs +++ b/pumpkin/src/block/blocks/redstone/pressure_plate/weighted.rs @@ -22,17 +22,14 @@ pub struct WeightedPressurePlateBlock; type PressurePlateProps = pumpkin_data::block_properties::LightWeightedPressurePlateLikeProperties; impl BlockMetadata for WeightedPressurePlateBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { + fn ids() -> Box<[u16]> { // light = Gold // heavy = Iron - &[ - "light_weighted_pressure_plate", - "heavy_weighted_pressure_plate", + [ + Block::LIGHT_WEIGHTED_PRESSURE_PLATE.id, + Block::HEAVY_WEIGHTED_PRESSURE_PLATE.id, ] + .into() } } diff --git a/pumpkin/src/block/blocks/redstone/redstone_torch.rs b/pumpkin/src/block/blocks/redstone/redstone_torch.rs index db21a4adc..9d94857dd 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_torch.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_torch.rs @@ -35,12 +35,8 @@ use super::get_redstone_power; pub struct RedstoneTorchBlock; impl BlockMetadata for RedstoneTorchBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::REDSTONE_TORCH.name, Block::REDSTONE_WALL_TORCH.name] + fn ids() -> Box<[u16]> { + [Block::REDSTONE_TORCH.id, Block::REDSTONE_WALL_TORCH.id].into() } } diff --git a/pumpkin/src/block/blocks/shulker_box.rs b/pumpkin/src/block/blocks/shulker_box.rs index 7fa84b437..cc7704943 100644 --- a/pumpkin/src/block/blocks/shulker_box.rs +++ b/pumpkin/src/block/blocks/shulker_box.rs @@ -7,7 +7,7 @@ use crate::block::{ }; use pumpkin_data::block_properties::BlockProperties; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; +use pumpkin_data::tag::{self}; use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{ @@ -44,12 +44,8 @@ impl ScreenHandlerFactory for ShulkerBoxScreenFactory { pub struct ShulkerBoxBlock; impl BlockMetadata for ShulkerBoxBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - get_tag_values(RegistryKey::Block, "minecraft:shulker_boxes").unwrap() + fn ids() -> Box<[u16]> { + tag::Block::MINECRAFT_SHULKER_BOXES.1.into() } } diff --git a/pumpkin/src/block/blocks/signs.rs b/pumpkin/src/block/blocks/signs.rs index 58ad71b90..8b0673c6a 100644 --- a/pumpkin/src/block/blocks/signs.rs +++ b/pumpkin/src/block/blocks/signs.rs @@ -5,8 +5,6 @@ use pumpkin_data::Block; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::EnumVariants; use pumpkin_data::block_properties::Integer0To15; -use pumpkin_data::tag::RegistryKey; -use pumpkin_data::tag::get_tag_values; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; @@ -160,7 +158,8 @@ impl BlockBehaviour for SignBlock { let mut item = args.item_stack.lock().await; - let Some(pumpkin_item) = args.server.item_registry.get_pumpkin_item(item.item) else { + let Some(pumpkin_item) = args.server.item_registry.get_pumpkin_item(item.item.id) + else { return BlockActionResult::PassToDefaultBlockAction; }; diff --git a/pumpkin/src/block/blocks/skull_block.rs b/pumpkin/src/block/blocks/skull_block.rs index 2588fd0fa..f2ddb0415 100644 --- a/pumpkin/src/block/blocks/skull_block.rs +++ b/pumpkin/src/block/blocks/skull_block.rs @@ -1,6 +1,7 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::{BlockBehaviour, BlockFuture, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs}; use crate::entity::EntityBase; +use pumpkin_data::Block; use pumpkin_data::block_properties::BlockProperties; use pumpkin_world::BlockStateId; use pumpkin_world::world::BlockFlags; @@ -10,19 +11,16 @@ type SkeletonSkullLikeProperties = pumpkin_data::block_properties::SkeletonSkull pub struct SkullBlock; impl BlockMetadata for SkullBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[ - "skeleton_skull", - "player_head", - "zombie_head", - "creeper_head", - "piglin_head", - "dragon_head", + fn ids() -> Box<[u16]> { + [ + Block::SKELETON_SKULL.id, + Block::PLAYER_HEAD.id, + Block::ZOMBIE_HEAD.id, + Block::CREEPER_HEAD.id, + Block::PIGLIN_HEAD.id, + Block::DRAGON_HEAD.id, ] + .into() } } diff --git a/pumpkin/src/block/blocks/slabs.rs b/pumpkin/src/block/blocks/slabs.rs index 12863a664..27d8ccea6 100644 --- a/pumpkin/src/block/blocks/slabs.rs +++ b/pumpkin/src/block/blocks/slabs.rs @@ -1,8 +1,6 @@ use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::SlabType; -use pumpkin_data::tag::RegistryKey; -use pumpkin_data::tag::get_tag_values; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_world::BlockStateId; diff --git a/pumpkin/src/block/blocks/stairs.rs b/pumpkin/src/block/blocks/stairs.rs index ccd1485d9..7c34dbc2f 100644 --- a/pumpkin/src/block/blocks/stairs.rs +++ b/pumpkin/src/block/blocks/stairs.rs @@ -2,9 +2,7 @@ use pumpkin_data::block_properties::BlockHalf; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::block_properties::StairShape; -use pumpkin_data::tag::RegistryKey; use pumpkin_data::tag::Taggable; -use pumpkin_data::tag::get_tag_values; use pumpkin_data::{BlockDirection, tag}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; diff --git a/pumpkin/src/block/blocks/torches.rs b/pumpkin/src/block/blocks/torches.rs index 99eeff72e..6083a9490 100644 --- a/pumpkin/src/block/blocks/torches.rs +++ b/pumpkin/src/block/blocks/torches.rs @@ -17,17 +17,14 @@ use crate::block::{ pub struct TorchBlock; impl BlockMetadata for TorchBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[ - Block::TORCH.name, - Block::SOUL_TORCH.name, - Block::WALL_TORCH.name, - Block::SOUL_WALL_TORCH.name, + fn ids() -> Box<[u16]> { + [ + Block::TORCH.id, + Block::SOUL_TORCH.id, + Block::WALL_TORCH.id, + Block::SOUL_WALL_TORCH.id, ] + .into() } } diff --git a/pumpkin/src/block/blocks/trapdoor.rs b/pumpkin/src/block/blocks/trapdoor.rs index c5be21e03..ef4ead355 100644 --- a/pumpkin/src/block/blocks/trapdoor.rs +++ b/pumpkin/src/block/blocks/trapdoor.rs @@ -6,7 +6,7 @@ use crate::world::World; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::{BlockHalf, BlockProperties}; use pumpkin_data::sound::{Sound, SoundCategory}; -use pumpkin_data::tag::{RegistryKey, Taggable, get_tag_values}; +use pumpkin_data::tag::Taggable; use pumpkin_data::{Block, tag}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; diff --git a/pumpkin/src/block/blocks/walls.rs b/pumpkin/src/block/blocks/walls.rs index 0a6b54fef..507e8c4c6 100644 --- a/pumpkin/src/block/blocks/walls.rs +++ b/pumpkin/src/block/blocks/walls.rs @@ -9,9 +9,7 @@ use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::block_properties::NorthWallShape; use pumpkin_data::block_properties::SouthWallShape; use pumpkin_data::block_properties::WestWallShape; -use pumpkin_data::tag::RegistryKey; use pumpkin_data::tag::Taggable; -use pumpkin_data::tag::get_tag_values; use pumpkin_data::{Block, tag}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; diff --git a/pumpkin/src/block/blocks/wither_skull.rs b/pumpkin/src/block/blocks/wither_skull.rs index 6412ca648..a29139d7f 100644 --- a/pumpkin/src/block/blocks/wither_skull.rs +++ b/pumpkin/src/block/blocks/wither_skull.rs @@ -12,12 +12,8 @@ use crate::{ pub struct WitherSkeletonSkullBlock; impl BlockMetadata for WitherSkeletonSkullBlock { - fn namespace(&self) -> &'static str { - "minecraft" - } - - fn ids(&self) -> &'static [&'static str] { - &[Block::WITHER_SKELETON_SKULL.name] + fn ids() -> Box<[u16]> { + [Block::WITHER_SKELETON_SKULL.id].into() } } diff --git a/pumpkin/src/block/fluid/lava.rs b/pumpkin/src/block/fluid/lava.rs index e66c64014..45baeed66 100644 --- a/pumpkin/src/block/fluid/lava.rs +++ b/pumpkin/src/block/fluid/lava.rs @@ -6,13 +6,12 @@ use pumpkin_data::{ fluid::{Falling, Fluid, FluidProperties, Level}, world::WorldEvent, }; -use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockStateId, tick::TickPriority, world::BlockFlags}; use crate::{ block::{ - BlockFuture, + BlockFuture, BlockMetadata, fluid::{FluidBehaviour, flowing::FluidFuture}, }, entity::EntityBase, @@ -22,9 +21,14 @@ use crate::{ use super::flowing::FlowingFluid; type FlowingFluidProperties = pumpkin_data::fluid::FlowingWaterLikeFluidProperties; -#[pumpkin_block("minecraft:flowing_lava")] pub struct FlowingLava; +impl BlockMetadata for FlowingLava { + fn ids() -> Box<[u16]> { + [Fluid::FLOWING_LAVA.id].into() + } +} + impl FlowingLava { async fn receive_neighbor_fluids( &self, diff --git a/pumpkin/src/block/fluid/water.rs b/pumpkin/src/block/fluid/water.rs index 27c0a3bf5..4b9b96921 100644 --- a/pumpkin/src/block/fluid/water.rs +++ b/pumpkin/src/block/fluid/water.rs @@ -1,21 +1,25 @@ use std::sync::Arc; use pumpkin_data::fluid::Fluid; -use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{BlockStateId, tick::TickPriority}; use crate::{ - block::{BlockFuture, fluid::FluidBehaviour}, + block::{BlockFuture, BlockMetadata, fluid::FluidBehaviour}, entity::EntityBase, world::World, }; use super::flowing::FlowingFluid; -#[pumpkin_block("minecraft:flowing_water")] pub struct FlowingWater; +impl BlockMetadata for FlowingWater { + fn ids() -> Box<[u16]> { + [Fluid::FLOWING_WATER.id].into() + } +} + const WATER_FLOW_SPEED: u8 = 5; impl FluidBehaviour for FlowingWater { diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index fcb6bd5ba..74d8e57e9 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -27,14 +27,7 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags}; use tokio::sync::Mutex; pub trait BlockMetadata { - fn namespace(&self) -> &'static str; - fn ids(&self) -> &'static [&'static str]; - fn names(&self) -> Vec { - self.ids() - .iter() - .map(|f| format!("{}:{}", self.namespace(), f)) - .collect() - } + fn ids() -> Box<[u16]>; } pub type BlockFuture<'a, T> = Pin + Send + 'a>>; diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs index bf32e92d5..c05aaef32 100644 --- a/pumpkin/src/block/registry.rs +++ b/pumpkin/src/block/registry.rs @@ -88,7 +88,6 @@ use crate::entity::EntityBase; use crate::entity::player::Player; use crate::server::Server; use crate::world::World; -use pumpkin_data::fluid; use pumpkin_data::fluid::Fluid; use pumpkin_data::item::Item; use pumpkin_data::{Block, BlockDirection, BlockState}; @@ -338,22 +337,20 @@ impl BlockRegistryExt for BlockRegistry { impl BlockRegistry { pub fn register(&mut self, block: T) { - let names = block.names(); + let ids = T::ids(); let val = Arc::new(block); - self.blocks.reserve(names.len()); - for i in names { - self.blocks - .insert(Block::from_name(i.as_str()).unwrap().id, val.clone()); + self.blocks.reserve(ids.len()); + for i in ids { + self.blocks.insert(i, val.clone()); } } pub fn register_fluid(&mut self, fluid: T) { - let names = fluid.names(); + let ids = T::ids(); let val = Arc::new(fluid); - self.fluids.reserve(names.len()); - for i in names { - self.fluids - .insert(fluid::get_fluid(i.as_str()).unwrap().id, val.clone()); + self.fluids.reserve(ids.len()); + for i in ids { + self.fluids.insert(i, val.clone()); } } diff --git a/pumpkin/src/command/commands/ban.rs b/pumpkin/src/command/commands/ban.rs index 3b0d83aa9..a27b0de2f 100644 --- a/pumpkin/src/command/commands/ban.rs +++ b/pumpkin/src/command/commands/ban.rs @@ -8,7 +8,7 @@ use crate::{ }, data::{ SaveJSONConfiguration, banlist_serializer::BannedPlayerEntry, - banned_player_data::BANNED_PLAYER_LIST, + banned_player::BANNED_PLAYER_LIST, }, entity::player::Player, net::DisconnectReason, diff --git a/pumpkin/src/command/commands/banip.rs b/pumpkin/src/command/commands/banip.rs index 9f6a3943e..035721f86 100644 --- a/pumpkin/src/command/commands/banip.rs +++ b/pumpkin/src/command/commands/banip.rs @@ -6,9 +6,7 @@ use crate::{ args::{Arg, ConsumedArgs, message::MsgArgConsumer, simple::SimpleArgConsumer}, tree::{CommandTree, builder::argument}, }, - data::{ - SaveJSONConfiguration, banlist_serializer::BannedIpEntry, banned_ip_data::BANNED_IP_LIST, - }, + data::{SaveJSONConfiguration, banlist_serializer::BannedIpEntry, banned_ip::BANNED_IP_LIST}, net::DisconnectReason, server::Server, }; diff --git a/pumpkin/src/command/commands/banlist.rs b/pumpkin/src/command/commands/banlist.rs index 1d337e823..1a5311679 100644 --- a/pumpkin/src/command/commands/banlist.rs +++ b/pumpkin/src/command/commands/banlist.rs @@ -4,7 +4,7 @@ use crate::{ args::{Arg, ConsumedArgs, simple::SimpleArgConsumer}, tree::{CommandTree, builder::argument}, }, - data::{banned_ip_data::BANNED_IP_LIST, banned_player_data::BANNED_PLAYER_LIST}, + data::{banned_ip::BANNED_IP_LIST, banned_player::BANNED_PLAYER_LIST}, }; use CommandError::InvalidConsumption; use pumpkin_util::text::TextComponent; diff --git a/pumpkin/src/command/commands/deop.rs b/pumpkin/src/command/commands/deop.rs index 6609cb2bd..e2e53df60 100644 --- a/pumpkin/src/command/commands/deop.rs +++ b/pumpkin/src/command/commands/deop.rs @@ -7,7 +7,7 @@ use crate::{ tree::CommandTree, tree::builder::argument, }, - data::{SaveJSONConfiguration, op_data::OPERATOR_CONFIG}, + data::{SaveJSONConfiguration, op::OPERATOR_CONFIG}, }; use CommandError::InvalidConsumption; use pumpkin_util::text::TextComponent; diff --git a/pumpkin/src/command/commands/op.rs b/pumpkin/src/command/commands/op.rs index 9ab0a4e41..f5ffb013a 100644 --- a/pumpkin/src/command/commands/op.rs +++ b/pumpkin/src/command/commands/op.rs @@ -7,7 +7,7 @@ use crate::{ tree::CommandTree, tree::builder::argument, }, - data::{SaveJSONConfiguration, op_data::OPERATOR_CONFIG}, + data::{SaveJSONConfiguration, op::OPERATOR_CONFIG}, }; use CommandError::InvalidConsumption; use pumpkin_config::op::Op; diff --git a/pumpkin/src/command/commands/pardon.rs b/pumpkin/src/command/commands/pardon.rs index 601b56996..684bba6ff 100644 --- a/pumpkin/src/command/commands/pardon.rs +++ b/pumpkin/src/command/commands/pardon.rs @@ -4,7 +4,7 @@ use crate::{ args::{Arg, ConsumedArgs, simple::SimpleArgConsumer}, tree::{CommandTree, builder::argument}, }, - data::{SaveJSONConfiguration, banned_player_data::BANNED_PLAYER_LIST}, + data::{SaveJSONConfiguration, banned_player::BANNED_PLAYER_LIST}, }; use CommandError::InvalidConsumption; use pumpkin_util::text::TextComponent; diff --git a/pumpkin/src/command/commands/pardonip.rs b/pumpkin/src/command/commands/pardonip.rs index bf70f7dac..55d48a49d 100644 --- a/pumpkin/src/command/commands/pardonip.rs +++ b/pumpkin/src/command/commands/pardonip.rs @@ -6,7 +6,7 @@ use crate::{ args::{Arg, ConsumedArgs, simple::SimpleArgConsumer}, tree::{CommandTree, builder::argument}, }, - data::{SaveJSONConfiguration, banned_ip_data::BANNED_IP_LIST}, + data::{SaveJSONConfiguration, banned_ip::BANNED_IP_LIST}, }; use CommandError::InvalidConsumption; use pumpkin_util::text::TextComponent; diff --git a/pumpkin/src/command/commands/whitelist.rs b/pumpkin/src/command/commands/whitelist.rs index 8d145145d..fd47486b4 100644 --- a/pumpkin/src/command/commands/whitelist.rs +++ b/pumpkin/src/command/commands/whitelist.rs @@ -17,7 +17,7 @@ use crate::{ }, data::{ LoadJSONConfiguration, SaveJSONConfiguration, - whitelist_data::{WHITELIST_CONFIG, WhitelistConfig}, + whitelist::{WHITELIST_CONFIG, WhitelistConfig}, }, net::DisconnectReason, server::Server, diff --git a/pumpkin/src/data/banned_ip_data.rs b/pumpkin/src/data/banned_ip.rs similarity index 100% rename from pumpkin/src/data/banned_ip_data.rs rename to pumpkin/src/data/banned_ip.rs diff --git a/pumpkin/src/data/banned_player_data.rs b/pumpkin/src/data/banned_player.rs similarity index 100% rename from pumpkin/src/data/banned_player_data.rs rename to pumpkin/src/data/banned_player.rs diff --git a/pumpkin/src/data/mod.rs b/pumpkin/src/data/mod.rs index 0e22de06b..d1db69ca2 100644 --- a/pumpkin/src/data/mod.rs +++ b/pumpkin/src/data/mod.rs @@ -4,13 +4,13 @@ use serde::{Deserialize, Serialize}; const DATA_FOLDER: &str = "data/"; -pub mod op_data; +pub mod op; pub mod banlist_serializer; -pub mod banned_ip_data; -pub mod banned_player_data; -pub mod player_server_data; -pub mod whitelist_data; +pub mod banned_ip; +pub mod banned_player; +pub mod player_server; +pub mod whitelist; pub trait LoadJSONConfiguration { #[must_use] diff --git a/pumpkin/src/data/op_data.rs b/pumpkin/src/data/op.rs similarity index 100% rename from pumpkin/src/data/op_data.rs rename to pumpkin/src/data/op.rs diff --git a/pumpkin/src/data/player_server_data.rs b/pumpkin/src/data/player_server.rs similarity index 99% rename from pumpkin/src/data/player_server_data.rs rename to pumpkin/src/data/player_server.rs index 436ab24de..19803d856 100644 --- a/pumpkin/src/data/player_server_data.rs +++ b/pumpkin/src/data/player_server.rs @@ -177,7 +177,7 @@ impl ServerPlayerData { #[cfg(test)] mod test { - use crate::data::player_server_data::ServerPlayerData; + use crate::data::player_server::ServerPlayerData; use pumpkin_nbt::compound::NbtCompound; use pumpkin_world::data::player_data::PlayerDataStorage; use std::time::Duration; diff --git a/pumpkin/src/data/whitelist_data.rs b/pumpkin/src/data/whitelist.rs similarity index 100% rename from pumpkin/src/data/whitelist_data.rs rename to pumpkin/src/data/whitelist.rs diff --git a/pumpkin/src/entity/hunger.rs b/pumpkin/src/entity/hunger.rs index 1b0b61ae8..d9ece69f6 100644 --- a/pumpkin/src/entity/hunger.rs +++ b/pumpkin/src/entity/hunger.rs @@ -1,18 +1,18 @@ use std::sync::Arc; -use crate::entity::NbtFuture; - use super::{EntityBase, NBTStorage, NBTStorageInit, player::Player}; +use crate::entity::NbtFuture; use crossbeam::atomic::AtomicCell; use pumpkin_data::damage::DamageType; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::Difficulty; -// TODO: This entire thing should be atomic, not individual fields +const MAX_FOOD: u8 = 20; +const EXHAUSTION_COST: f32 = 4.0; +const MAX_EXHAUSTION: f32 = 40.0; + pub struct HungerManager { - /// The current hunger level. pub level: AtomicCell, - /// The food saturation level. pub saturation: AtomicCell, pub exhaustion: AtomicCell, pub tick_timer: AtomicCell, @@ -21,7 +21,7 @@ pub struct HungerManager { impl Default for HungerManager { fn default() -> Self { Self { - level: AtomicCell::new(20), + level: AtomicCell::new(MAX_FOOD), saturation: AtomicCell::new(5.0), exhaustion: AtomicCell::new(0.0), tick_timer: AtomicCell::new(0), @@ -31,76 +31,113 @@ impl Default for HungerManager { impl HungerManager { pub async fn tick(&self, player: &Arc) { - let saturation = self.saturation.load(); - let level = self.level.load(); - let exhaustion = self.exhaustion.load(); - let health = player.living_entity.health.load(); + let mut level = self.level.load(); + let mut saturation = self.saturation.load(); + let mut exhaustion = self.exhaustion.load(); + let mut timer = self.tick_timer.load(); + let difficulty = player.world().level_info.read().await.difficulty; - // Decrease hunger level on exhaustion - if level != 0 && exhaustion > 4.0 { - self.exhaustion.store(exhaustion - 4.0); + let health = player.living_entity.health.load(); + let can_heal = player.can_food_heal(); + + let mut needs_sync = false; + let mut heal_amount = 0.0; + let mut damage_amount = 0.0; + + if exhaustion > EXHAUSTION_COST { + exhaustion -= EXHAUSTION_COST; if saturation > 0.0 { - self.saturation.store((saturation - 1.0).max(0.0)); + saturation = (saturation - 1.0).max(0.0); } else if difficulty != Difficulty::Peaceful { - self.level.store(level - 1); - player.send_health().await; + level = level.saturating_sub(1); } + needs_sync = true; } - // Heal when hunger is full - let natural_regen = true; // TODO: Get the actual value when this will be implemented. - if natural_regen && saturation > 0.0 && player.can_food_heal() && level >= 20 { - self.tick_timer.fetch_add(1); - if self.tick_timer.load() >= 10 { - let saturation = saturation.min(6.0); - player.heal(saturation / 6.0).await; - self.add_exhaustion(saturation); - self.tick_timer.store(0); + let natural_regen = true; // TODO: GameRule check + + if natural_regen && saturation > 0.0 && can_heal && level >= 20 { + timer += 1; + if timer >= 10 { + let cost = saturation.min(6.0); + saturation -= cost; + exhaustion += cost; + heal_amount = cost / 6.0; + timer = 0; + needs_sync = true; } - } else if natural_regen && level >= 18 && player.can_food_heal() { - self.tick_timer.fetch_add(1); - if self.tick_timer.load() >= 80 { - player.heal(1.0).await; - self.add_exhaustion(6.0); - self.tick_timer.store(0); + } else if natural_regen && level >= 18 && can_heal { + timer += 1; + if timer >= 80 { + heal_amount = 1.0; + exhaustion += 6.0; + timer = 0; + needs_sync = true; } } else if level == 0 { - self.tick_timer.fetch_add(1); - if self.tick_timer.load() >= 80 { - if (health > 10.0) - || (difficulty == Difficulty::Hard) - || (health > 1.0 && difficulty == Difficulty::Normal) - { - player.damage(&**player, 1.0, DamageType::STARVE).await; + timer += 1; + if timer >= 80 { + timer = 0; + let should_starve = match difficulty { + Difficulty::Peaceful => false, + Difficulty::Easy => health > 10.0, + Difficulty::Normal => health > 1.0, + Difficulty::Hard => true, + }; + + if should_starve { + damage_amount = 1.0; } self.tick_timer.store(0); } } else { - self.tick_timer.store(0); + timer = 0; + } + + if needs_sync || timer != self.tick_timer.load() { + self.level.store(level); + self.saturation.store(saturation); + self.exhaustion.store(exhaustion); + self.tick_timer.store(timer); + } + + if needs_sync { + player.send_health().await; + } + if heal_amount > 0.0 { + player.heal(heal_amount).await; + } + if damage_amount > 0.0 { + player + .damage(&**player, damage_amount, DamageType::STARVE) + .await; } } - pub async fn add_modifier(&self, player: &Player, food: u8, saturation_modifier: f32) { - let saturation = f32::from(food) * saturation_modifier * 2.0; - self.level.store(food + self.level.load()); - self.saturation.store(saturation + self.saturation.load()); - player.send_health().await; - } + pub async fn eat(&self, player: &Player, food: u8, saturation_modifier: f32) { + let added_saturation = f32::from(food) * saturation_modifier * 2.0; - pub async fn eat(&self, player: &Player, food: u8, saturation: f32) { - self.level.store(food + self.level.load()); - self.saturation.store(saturation + self.saturation.load()); + let current_level = self.level.load(); + let current_sat = self.saturation.load(); + + let new_level = (current_level + food).min(MAX_FOOD); + + let new_sat = (current_sat + added_saturation).min(f32::from(new_level)); + + self.level.store(new_level); + self.saturation.store(new_sat); player.send_health().await; } pub fn add_exhaustion(&self, exhaustion: f32) { + let current = self.exhaustion.load(); self.exhaustion - .store((self.exhaustion.load() + exhaustion).min(40.0)); + .store((current + exhaustion).min(MAX_EXHAUSTION)); } pub fn restart(&self) { - self.level.store(20); + self.level.store(MAX_FOOD); self.saturation.store(5.0); self.exhaustion.store(0.0); self.tick_timer.store(0); @@ -108,7 +145,6 @@ impl HungerManager { } impl NBTStorage for HungerManager { - // TODO: Proper value checks fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async { nbt.put_int("foodLevel", self.level.load().into()); diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 6b1d1e5b8..e7857e0a3 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -77,7 +77,7 @@ use pumpkin_world::level::{Level, SyncChunk, SyncEntityChunk}; use crate::block::blocks::bed::BedBlock; use crate::command::client_suggestions; use crate::command::dispatcher::CommandDispatcher; -use crate::data::op_data::OPERATOR_CONFIG; +use crate::data::op::OPERATOR_CONFIG; use crate::entity::{EntityBaseFuture, NbtFuture, TeleportFuture}; use crate::net::{ClientPlatform, GameProfile}; use crate::net::{DisconnectReason, PlayerConfig}; @@ -1210,47 +1210,76 @@ impl Player { .await; } ClientPlatform::Bedrock(bedrock) => { - let mut ability_value = 0; - let abilities = &self.abilities.lock().await; + let abilities = self.abilities.lock().await; + let is_op = self.permission_lvl.load() == PermissionLvl::Four; + let is_spectator = self.gamemode.load() == GameMode::Spectator; - if abilities.invulnerable { - ability_value |= 1 << Ability::Invulnerable as u32; - } + // 1. Permission Mapping + let player_perm = if is_op { 2 } else { 1 }; // 1: Member, 2: Operator + let command_perm = u8::from(is_op); // 0: Normal, 1: Operator - if abilities.flying { - ability_value |= 1 << Ability::Flying as u32; - } + // 2. Build the Ability Bitmask + let mut ability_value: u32 = 0; - if abilities.allow_flying { - ability_value |= 1 << Ability::MayFly as u32; - } + // Helper closure to set bits using your enum + let mut set_ability = |ability: Ability, enabled: bool| { + if enabled { + ability_value |= 1 << (ability as u32); + } + }; - if abilities.creative { - ability_value |= 1 << Ability::OperatorCommands as u32; - ability_value |= 1 << Ability::Teleport as u32; - ability_value |= 1 << Ability::Invulnerable as u32; - } + // Base Permissions + set_ability(Ability::MayFly, abilities.allow_flying); + set_ability(Ability::Flying, abilities.flying); + set_ability( + Ability::Invulnerable, + abilities.invulnerable || abilities.creative, + ); - // Todo: Integrate this into the system - ability_value |= 1 << Ability::AttackMobs as u32; - ability_value |= 1 << Ability::AttackPlayers as u32; - ability_value |= 1 << Ability::Build as u32; - ability_value |= 1 << Ability::DoorsAndSwitches as u32; - ability_value |= 1 << Ability::Instabuild as u32; - ability_value |= 1 << Ability::Mine as u32; + // Operator Specifics + set_ability(Ability::OperatorCommands, is_op); + set_ability(Ability::Teleport, is_op); - let packet = CUpdateAbilities { - target_player_raw_id: self.entity_id().into(), - player_permission: 2, - command_permission: 4, - layers: vec![AbilityLayer { + // Interaction Permissions (Disabled for Spectators) + let can_interact = !is_spectator; + set_ability(Ability::Build, can_interact); + set_ability(Ability::Mine, can_interact); + set_ability(Ability::DoorsAndSwitches, can_interact); + set_ability(Ability::OpenContainers, can_interact); + set_ability(Ability::AttackPlayers, can_interact); + set_ability(Ability::AttackMobs, can_interact); + + // Creative/Spectator Extras + set_ability(Ability::Instabuild, abilities.creative); + set_ability(Ability::NoClip, is_spectator); + + // 3. Construct the Layers + let mut layers = vec![AbilityLayer { + serialized_layer: 0, // LAYER_BASE + // 0x3FFFF defines the first 18 bits as "provided" by this packet + abilities_set: (1 << Ability::AbilityCount as u32) - 1, + ability_value, + fly_speed: 0.05, + vertical_fly_speed: 1.0, + walk_speed: 0.1, + }]; + + if is_spectator { + layers.push(AbilityLayer { serialized_layer: 1, - abilities_set: (1 << Ability::AbilityCount as u32) - 1, - ability_value, + abilities_set: 1 << (Ability::Flying as u32), + ability_value: 1 << (Ability::Flying as u32), fly_speed: 0.05, vertical_fly_speed: 1.0, walk_speed: 0.1, - }], + }); + } + + let packet = CUpdateAbilities { + target_player_raw_id: self.entity_id().into(), + player_permission: player_perm, + command_permission: command_perm, + layers, }; bedrock.send_game_packet(&packet).await; diff --git a/pumpkin/src/item/registry.rs b/pumpkin/src/item/registry.rs index 0023f357b..2380f03d9 100644 --- a/pumpkin/src/item/registry.rs +++ b/pumpkin/src/item/registry.rs @@ -14,7 +14,7 @@ use super::{ItemBehaviour, ItemMetadata}; #[derive(Default)] pub struct ItemRegistry { - items: HashMap<&'static Item, Arc>, + items: HashMap>, } impl ItemRegistry { @@ -22,12 +22,12 @@ impl ItemRegistry { let val = Arc::new(item); self.items.reserve(T::ids().len()); for i in T::ids() { - self.items.insert(Item::from_id(i).unwrap(), val.clone()); + self.items.insert(i, val.clone()); } } pub async fn on_use(&self, item: &Item, player: &Player) { - let pumpkin_item = self.get_pumpkin_item(item); + let pumpkin_item = self.get_pumpkin_item(item.id); if let Some(pumpkin_item) = pumpkin_item { pumpkin_item.normal_use(item, player).await; } @@ -44,7 +44,7 @@ impl ItemRegistry { block: &Block, server: &Server, ) { - let pumpkin_item = self.get_pumpkin_item(stack.item); + let pumpkin_item = self.get_pumpkin_item(stack.item.id); if let Some(pumpkin_item) = pumpkin_item { pumpkin_item .use_on_block(stack, player, location, face, cursor_pos, block, server) @@ -58,14 +58,14 @@ impl ItemRegistry { player: &Player, entity: Arc, ) { - let pumpkin_item = self.get_pumpkin_item(stack.item); + let pumpkin_item = self.get_pumpkin_item(stack.item.id); if let Some(pumpkin_item) = pumpkin_item { pumpkin_item.use_on_entity(stack, player, entity).await; } } pub fn can_mine(&self, item: &Item, player: &Player) -> bool { - let pumpkin_block = self.get_pumpkin_item(item); + let pumpkin_block = self.get_pumpkin_item(item.id); if let Some(pumpkin_block) = pumpkin_block { return pumpkin_block.can_mine(player); } @@ -73,7 +73,7 @@ impl ItemRegistry { } #[must_use] - pub fn get_pumpkin_item(&self, item: &Item) -> Option<&dyn ItemBehaviour> { - self.items.get(item).map(|value| &**value) + pub fn get_pumpkin_item(&self, item: u16) -> Option<&Arc> { + self.items.get(&item) } } diff --git a/pumpkin/src/net/mod.rs b/pumpkin/src/net/mod.rs index e2c25d408..c9918a2dc 100644 --- a/pumpkin/src/net/mod.rs +++ b/pumpkin/src/net/mod.rs @@ -6,8 +6,8 @@ use std::{ use crate::{ data::{ - banned_ip_data::BANNED_IP_LIST, banned_player_data::BANNED_PLAYER_LIST, - op_data::OPERATOR_CONFIG, whitelist_data::WHITELIST_CONFIG, + banned_ip::BANNED_IP_LIST, banned_player::BANNED_PLAYER_LIST, op::OPERATOR_CONFIG, + whitelist::WHITELIST_CONFIG, }, entity::player::ChatMode, net::{bedrock::BedrockClient, java::JavaClient}, diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 327eccebf..49b3ebafb 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -1,7 +1,7 @@ use crate::block::registry::BlockRegistry; use crate::command::commands::default_dispatcher; use crate::command::commands::defaultgamemode::DefaultGamemode; -use crate::data::player_server_data::ServerPlayerData; +use crate::data::player_server::ServerPlayerData; use crate::entity::{EntityBase, NBTStorage}; use crate::item::registry::ItemRegistry; use crate::net::{ClientPlatform, DisconnectReason, EncryptionError, GameProfile, PlayerConfig}; diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 7dc139c46..a854deb8d 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -1,4 +1,3 @@ -use std::num::NonZeroU32; use std::pin::Pin; use std::sync::Weak; use std::sync::atomic::Ordering::Relaxed; @@ -57,8 +56,6 @@ use pumpkin_data::{ use pumpkin_data::{BlockDirection, BlockState}; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed}; -use pumpkin_protocol::bedrock::client::chunk_radius_update::CChunkRadiusUpdate; -use pumpkin_protocol::bedrock::client::network_chunk_publisher_update::CNetworkChunkPublisherUpdate; use pumpkin_protocol::bedrock::client::start_game::CStartGame; use pumpkin_protocol::bedrock::frame_set::FrameSet; use pumpkin_protocol::java::client::play::CPlayerSpawnPosition; @@ -1175,6 +1172,24 @@ impl World { let level_info = server.level_info.read().await; let weather = self.weather.lock().await; let runtime_id = player.entity_id() as u64; + let (position, yaw, pitch) = if player.has_played_before.load(Ordering::Relaxed) { + let position = player.position(); + let yaw = player.living_entity.entity.yaw.load(); //info.spawn_angle; + let pitch = player.living_entity.entity.pitch.load(); + + (position, yaw, pitch) + } else { + let info = &self.level_info.read().await; + let spawn_position = Vector2::new(info.spawn_x, info.spawn_z); + let pos_y = self.get_top_block(spawn_position).await + 1; // +1 to spawn on top of the block + + let position = Vector3::new( + f64::from(info.spawn_x) + 0.5, + f64::from(pos_y), + f64::from(info.spawn_z) + 0.5, + ); + (position, info.spawn_yaw, info.spawn_pitch) + }; // Todo make the data less spread let level_settings = LevelSettings { seed: self.level.seed.0, @@ -1253,22 +1268,22 @@ impl World { entity_id: VarLong(runtime_id as _), runtime_entity_id: VarULong(runtime_id), player_gamemode: player.gamemode.load(), - position: Vector3::new(0.0, 200.0, 0.0), - pitch: 0.0, - yaw: 0.0, + position: Vector3::new(position.x as f32, position.y as f32, position.z as f32), + pitch, + yaw, level_settings, level_id: String::new(), level_name: "Pumpkin world".to_string(), premium_world_template_id: String::new(), is_trial: false, - rewind_history_size: VarInt(40), - server_authoritative_block_breaking: false, + rewind_history_size: VarInt(0), + server_authoritative_block_breaking: true, current_level_time: self.level_time.lock().await.world_age as _, enchantment_seed: VarInt(0), block_properties_size: VarUInt(0), // TODO Make this unique multiplayer_correlation_id: Uuid::default().to_string(), - enable_itemstack_net_manager: false, + enable_itemstack_net_manager: true, // TODO Make this description better! // This gets send from the client to mojang for telemetry server_version: "Pumpkin Rust Server".to_string(), @@ -1278,18 +1293,13 @@ impl World { compound_end: 0, block_registry_checksum: 0, - world_template_id: Uuid::default(), + world_template_id: Uuid::nil(), // TODO The client needs extra biome data for this enable_clientside_generation: false, blocknetwork_ids_are_hashed: false, server_auth_sounds: false, }) .await; - client - .send_game_packet(&CChunkRadiusUpdate { - chunk_radius: VarInt(player.config.read().await.view_distance.get().into()), - }) - .await; chunker::update_position(&player).await; client .send_game_packet(&CreativeContent { @@ -1302,48 +1312,91 @@ impl World { }) .await; - let mut frame_set = FrameSet::default(); - client - .write_game_packet_to_set( - &CNetworkChunkPublisherUpdate::new( - BlockPos::new(0, 200, 0), - NonZeroU32::from(player.config.read().await.view_distance).into(), - ), - &mut frame_set, - ) - .await; - - client - .write_game_packet_to_set( - &CUpdateAttributes { - runtime_id: VarULong(runtime_id), - attributes: vec![Attribute { - min_value: 0.0, - max_value: f32::MAX, - current_value: 0.1, - default_min_value: 0.0, - default_max_value: f32::MAX, - default_value: 0.1, - name: "minecraft:movement".to_string(), - modifiers_list_size: VarUInt(0), - }], - player_tick: VarULong(0), - }, - &mut frame_set, - ) - .await; - - client - .write_game_packet_to_set(&CPlayStatus::PlayerSpawn, &mut frame_set) - .await; - client.send_frame_set(frame_set, 0x84).await; - { let mut abilities = player.abilities.lock().await; abilities.set_for_gamemode(player.gamemode.load()); }; player.send_abilities_update().await; + + let mut frame_set = FrameSet::default(); + + // https://github.com/pmmp/PocketMine-MP/blob/0b6d8f8cb2aaa05ffad0b6386bd88d73ef54b395/src/entity/AttributeFactory.php#L34 + client + .write_game_packet_to_set( + &CUpdateAttributes { + runtime_id: VarULong(runtime_id), + attributes: vec![ + Attribute { + min_value: 0.0, + max_value: 3.402_823_5E38, + current_value: 0.1, + default_min_value: 0.0, + default_max_value: 3.402_823_5E38, + default_value: 0.1, + name: "minecraft:movement".to_string(), + modifiers_list_size: VarUInt(0), + }, + Attribute { + min_value: 0.0, + max_value: 3.402_823_5E38, + current_value: 0.02, + default_min_value: 0.0, + default_max_value: 3.402_823_5E38, + default_value: 0.02, + name: "minecraft:underwater_movement".to_string(), + modifiers_list_size: VarUInt(0), + }, + Attribute { + min_value: 0.0, + max_value: 1.0, + current_value: 0.08, + default_min_value: 0.0, + default_max_value: 1.0, + default_value: 0.08, + name: "minecraft:gravity".to_string(), + modifiers_list_size: VarUInt(0), + }, + Attribute { + min_value: 0.0, + max_value: 400.0, + current_value: 400.0, + default_min_value: 0.0, + default_max_value: 400.0, + default_value: 400.0, + name: "minecraft:air".to_string(), + modifiers_list_size: VarUInt(0), + }, + Attribute { + min_value: 0.0, + max_value: 20.0, + current_value: 20.0, + default_min_value: 0.0, + default_max_value: 20.0, + default_value: 20.0, + name: "minecraft:health".to_string(), + modifiers_list_size: VarUInt(0), + }, + Attribute { + min_value: 0.0, + max_value: 20.0, + current_value: 20.0, + default_min_value: 0.0, + default_max_value: 20.0, + default_value: 20.0, + name: "minecraft:player.hunger".to_string(), + modifiers_list_size: VarUInt(0), + }, + ], + player_tick: VarULong(0), + }, + &mut frame_set, + ) + .await; + client + .write_game_packet_to_set(&CPlayStatus::PlayerSpawn, &mut frame_set) + .await; + client.send_frame_set(frame_set, 0x84).await; } #[expect(clippy::too_many_lines)]