feat(bedrock): add creative content
This commit is contained in:
parent
02f6c8994e
commit
8ce76261ac
33
Cargo.lock
generated
33
Cargo.lock
generated
@ -270,9 +270,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.11.1"
|
version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cap-fs-ext"
|
name = "cap-fs-ext"
|
||||||
@ -471,9 +471,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cmov"
|
name = "cmov"
|
||||||
version = "0.5.3"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
|
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cobs"
|
name = "cobs"
|
||||||
@ -919,9 +919,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-bigint"
|
name = "crypto-bigint"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42a0d26b245348befa0c121944541476763dcc46ede886c88f9d12e1697d27c3"
|
checksum = "97bb4a855e3b10f84c4e7e895a7de01db7f9a7b7eb7f73ed9773fd52ac686451"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cpubits",
|
"cpubits",
|
||||||
"ctutils",
|
"ctutils",
|
||||||
@ -956,7 +956,7 @@ version = "0.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21f41f23de7d24cdbda7f0c4d9c0351f99a4ceb258ef30e5c1927af8987ffe5a"
|
checksum = "21f41f23de7d24cdbda7f0c4d9c0351f99a4ceb258ef30e5c1927af8987ffe5a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-bigint 0.7.3",
|
"crypto-bigint 0.7.4",
|
||||||
"libm",
|
"libm",
|
||||||
"rand_core 0.10.1",
|
"rand_core 0.10.1",
|
||||||
]
|
]
|
||||||
@ -1890,6 +1890,15 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@ -2841,7 +2850,7 @@ dependencies = [
|
|||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"colored",
|
"colored",
|
||||||
"crypto-bigint 0.7.3",
|
"crypto-bigint 0.7.4",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"enum_dispatch",
|
"enum_dispatch",
|
||||||
"md5",
|
"md5",
|
||||||
@ -2877,7 +2886,7 @@ dependencies = [
|
|||||||
"enum_dispatch",
|
"enum_dispatch",
|
||||||
"flate2",
|
"flate2",
|
||||||
"futures",
|
"futures",
|
||||||
"itertools 0.14.0",
|
"itertools 0.15.0",
|
||||||
"lz4-java-wrc",
|
"lz4-java-wrc",
|
||||||
"pumpkin-config",
|
"pumpkin-config",
|
||||||
"pumpkin-data",
|
"pumpkin-data",
|
||||||
@ -3067,7 +3076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3caf"
|
checksum = "30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3caf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid 0.10.2",
|
"const-oid 0.10.2",
|
||||||
"crypto-bigint 0.7.3",
|
"crypto-bigint 0.7.4",
|
||||||
"crypto-primes",
|
"crypto-primes",
|
||||||
"digest 0.11.3",
|
"digest 0.11.3",
|
||||||
"pkcs1",
|
"pkcs1",
|
||||||
@ -3950,9 +3959,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.20.0"
|
version = "1.20.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
|
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ucd-trie"
|
name = "ucd-trie"
|
||||||
|
|||||||
@ -102,7 +102,7 @@ syn = { version = "2.0", default-features = false, features = ["printing"] }
|
|||||||
|
|
||||||
thiserror = { version = "2.0", default-features = false }
|
thiserror = { version = "2.0", default-features = false }
|
||||||
|
|
||||||
bytes = { version = "1.11", default-features = false, features = ["std"] }
|
bytes = { version = "1.12", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
# Concurrency/Parallelism and Synchronization
|
# Concurrency/Parallelism and Synchronization
|
||||||
futures = { version = "0.3", default-features = false, features = ["executor"] }
|
futures = { version = "0.3", default-features = false, features = ["executor"] }
|
||||||
@ -128,7 +128,7 @@ crc-fast = { version = "1.10.0", default-features = false, features = ["std"] }
|
|||||||
criterion = { version = "0.8", default-features = false }
|
criterion = { version = "0.8", default-features = false }
|
||||||
crossbeam-utils = { version = "0.8.21", default-features = false, features = ["std"] }
|
crossbeam-utils = { version = "0.8.21", default-features = false, features = ["std"] }
|
||||||
crossfire = { version = "3.1.16", default-features = false, features = ["compat"] }
|
crossfire = { version = "3.1.16", default-features = false, features = ["compat"] }
|
||||||
crypto-bigint = { version = "0.7.3", default-features = false }
|
crypto-bigint = { version = "0.7.4", default-features = false }
|
||||||
dashmap = { version = "6.2", default-features = false }
|
dashmap = { version = "6.2", default-features = false }
|
||||||
ecdsa = { version = "0.16.9", default-features = false, features = ["std"] }
|
ecdsa = { version = "0.16.9", default-features = false, features = ["std"] }
|
||||||
either = { version = "1.16.0", default-features = false }
|
either = { version = "1.16.0", default-features = false }
|
||||||
@ -137,7 +137,7 @@ flate2 = { version = "1.1.9", default-features = false, features = ["rust_backen
|
|||||||
heck = { version = "0.5", default-features = false }
|
heck = { version = "0.5", default-features = false }
|
||||||
hmac = { version = "=0.13.0", default-features = false }
|
hmac = { version = "=0.13.0", default-features = false }
|
||||||
indexmap = { version = "2.14", default-features = false, features = ["std"] }
|
indexmap = { version = "2.14", default-features = false, features = ["std"] }
|
||||||
itertools = { version = "0.14.0", default-features = false, features = ["use_std"] }
|
itertools = { version = "0.15.0", default-features = false, features = ["use_std"] }
|
||||||
libloading = { version = "0.9", default-features = false, features = ["std"] }
|
libloading = { version = "0.9", default-features = false, features = ["std"] }
|
||||||
lru = { version = "0.18.0", default-features = false }
|
lru = { version = "0.18.0", default-features = false }
|
||||||
lz4-java-wrc = { version = "0.2.0", default-features = false, features = ["lz4_flex"] }
|
lz4-java-wrc = { version = "0.2.0", default-features = false, features = ["lz4_flex"] }
|
||||||
|
|||||||
131
pumpkin-codegen/src/bedrock_creative.rs
Normal file
131
pumpkin-codegen/src/bedrock_creative.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use std::{collections::HashMap, fs, io::Cursor};
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_repr::Deserialize_repr;
|
||||||
|
|
||||||
|
#[derive(Deserialize_repr, Debug)]
|
||||||
|
#[repr(i32)]
|
||||||
|
enum BedrockItemVersion {
|
||||||
|
Legacy = 0,
|
||||||
|
DataDriven = 1,
|
||||||
|
None = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||||
|
struct BedrockRuntimeItemState {
|
||||||
|
name: String,
|
||||||
|
id: i16,
|
||||||
|
version: BedrockItemVersion,
|
||||||
|
component_based: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build() -> TokenStream {
|
||||||
|
let be_runtime_item_states: Vec<BedrockRuntimeItemState> = serde_json::from_str(
|
||||||
|
&fs::read_to_string("../assets/bedrock/runtime_item_states.json").unwrap(),
|
||||||
|
)
|
||||||
|
.expect("Failed to parse bedrock/runtime_item_states.json");
|
||||||
|
|
||||||
|
let bedrock_items_map: HashMap<String, i16> = be_runtime_item_states
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| (item.name, item.id))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let nbt_bytes = fs::read("../assets/bedrock/creative_items.nbt")
|
||||||
|
.expect("Failed to read bedrock/creative_items.nbt");
|
||||||
|
let mut cursor = Cursor::new(nbt_bytes);
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct CreativeGroupEntry {
|
||||||
|
category: i32,
|
||||||
|
name: String,
|
||||||
|
icon: Option<CreativeItemEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct CreativeItemEntry {
|
||||||
|
name: String,
|
||||||
|
meta: Option<i16>,
|
||||||
|
group_index: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct CreativeData {
|
||||||
|
groups: Vec<CreativeGroupEntry>,
|
||||||
|
items: Vec<CreativeItemEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let data: CreativeData = pumpkin_nbt::deserializer::from_bytes_bedrock(&mut cursor)
|
||||||
|
.expect("Failed to deserialize creative_items.nbt");
|
||||||
|
|
||||||
|
let mut group_tokens = Vec::new();
|
||||||
|
for g in &data.groups {
|
||||||
|
let category = g.category;
|
||||||
|
let name = &g.name;
|
||||||
|
let mut icon_item_id = 0i16;
|
||||||
|
let mut icon_item_aux_value = 0u32;
|
||||||
|
|
||||||
|
if let Some(ref icon) = g.icon {
|
||||||
|
if let Some(&id) = bedrock_items_map.get(icon.name.as_str()) {
|
||||||
|
icon_item_id = id;
|
||||||
|
icon_item_aux_value = icon.meta.unwrap_or(0) as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group_tokens.push(quote! {
|
||||||
|
CreativeGroup {
|
||||||
|
category: #category,
|
||||||
|
name: #name,
|
||||||
|
icon_item_id: #icon_item_id,
|
||||||
|
icon_item_aux_value: #icon_item_aux_value,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Build entry definitions TokenStream
|
||||||
|
let mut entry_tokens = Vec::new();
|
||||||
|
for item in &data.items {
|
||||||
|
if let Some(&id) = bedrock_items_map.get(item.name.as_str()) {
|
||||||
|
let item_aux_value = item.meta.unwrap_or(0) as u32;
|
||||||
|
let group_index = item.group_index.unwrap_or(0) as u32;
|
||||||
|
|
||||||
|
entry_tokens.push(quote! {
|
||||||
|
CreativeEntry {
|
||||||
|
item_id: #id,
|
||||||
|
item_aux_value: #item_aux_value,
|
||||||
|
group_index: #group_index,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let groups_len = group_tokens.len();
|
||||||
|
let entries_len = entry_tokens.len();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct CreativeGroup {
|
||||||
|
pub category: i32,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub icon_item_id: i16,
|
||||||
|
pub icon_item_aux_value: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct CreativeEntry {
|
||||||
|
pub item_id: i16,
|
||||||
|
pub item_aux_value: u32,
|
||||||
|
pub group_index: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CREATIVE_GROUPS: &[CreativeGroup; #groups_len] = &[
|
||||||
|
#(#group_tokens),*
|
||||||
|
];
|
||||||
|
|
||||||
|
pub const CREATIVE_ENTRIES: &[CreativeEntry; #entries_len] = &[
|
||||||
|
#(#entry_tokens),*
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1146,6 +1146,11 @@ pub fn build() -> TokenStream {
|
|||||||
all_java_item_ids.push(id_lit);
|
all_java_item_ids.push(id_lit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bedrock_id_by_key: std::collections::HashMap<String, i16> = bedrock_item_definitions
|
||||||
|
.iter()
|
||||||
|
.map(|item| (item.registry_key.clone(), item.id))
|
||||||
|
.collect();
|
||||||
|
|
||||||
for item in bedrock_item_definitions {
|
for item in bedrock_item_definitions {
|
||||||
let const_ident = format_ident!(
|
let const_ident = format_ident!(
|
||||||
"{}",
|
"{}",
|
||||||
@ -1211,6 +1216,35 @@ pub fn build() -> TokenStream {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut bedrock_to_java_arms = TokenStream::new();
|
||||||
|
let mut seen_bedrock_pairs = std::collections::HashSet::new();
|
||||||
|
|
||||||
|
let mut bedrock_id_fallback_arms = TokenStream::new();
|
||||||
|
let mut seen_bedrock_ids = std::collections::HashSet::new();
|
||||||
|
|
||||||
|
for (java_name, mapping) in &be_item_remaps {
|
||||||
|
let java_const_ident = format_ident!("{}", java_name.to_shouty_snake_case());
|
||||||
|
let bedrock_id = *bedrock_id_by_key.get(&mapping.identifier).unwrap_or_else(|| {
|
||||||
|
panic!("Missing bedrock item for identifier: {}", mapping.identifier)
|
||||||
|
});
|
||||||
|
let bedrock_data = mapping.data;
|
||||||
|
|
||||||
|
if seen_bedrock_pairs.insert((bedrock_id, bedrock_data)) {
|
||||||
|
let id_lit = syn::LitInt::new(&bedrock_id.to_string(), proc_macro2::Span::call_site());
|
||||||
|
let data_lit = syn::LitInt::new(&bedrock_data.to_string(), proc_macro2::Span::call_site());
|
||||||
|
bedrock_to_java_arms.extend(quote! {
|
||||||
|
(#id_lit, #data_lit) => Some(&Self::#java_const_ident),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if seen_bedrock_ids.insert(bedrock_id) {
|
||||||
|
let id_lit = syn::LitInt::new(&bedrock_id.to_string(), proc_macro2::Span::call_site());
|
||||||
|
bedrock_id_fallback_arms.extend(quote! {
|
||||||
|
#id_lit => Some(&Self::#java_const_ident),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[allow(clippy::wildcard_imports, clippy::enum_glob_use, clippy::too_many_lines)]
|
#[allow(clippy::wildcard_imports, clippy::enum_glob_use, clippy::too_many_lines)]
|
||||||
use crate::data_component::DataComponent::*;
|
use crate::data_component::DataComponent::*;
|
||||||
@ -1371,6 +1405,17 @@ pub fn build() -> TokenStream {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_bedrock(bedrock_id: i16, bedrock_data: u32) -> Option<&'static Self> {
|
||||||
|
match (bedrock_id, bedrock_data) {
|
||||||
|
#bedrock_to_java_arms
|
||||||
|
_ => match bedrock_id {
|
||||||
|
#bedrock_id_fallback_arms
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use std::{
|
|||||||
|
|
||||||
mod advancement;
|
mod advancement;
|
||||||
mod attributes;
|
mod attributes;
|
||||||
|
mod bedrock_creative;
|
||||||
mod biome;
|
mod biome;
|
||||||
mod bitsets;
|
mod bitsets;
|
||||||
mod block;
|
mod block;
|
||||||
@ -78,6 +79,7 @@ pub fn main() {
|
|||||||
|
|
||||||
let mut build_functions: Vec<(BuilderFn, &str)> = vec![
|
let mut build_functions: Vec<(BuilderFn, &str)> = vec![
|
||||||
(advancement::build, "advancement.rs"),
|
(advancement::build, "advancement.rs"),
|
||||||
|
(bedrock_creative::build, "bedrock_creative.rs"),
|
||||||
(packet::build, "packet.rs"),
|
(packet::build, "packet.rs"),
|
||||||
(screen::build, "screen.rs"),
|
(screen::build, "screen.rs"),
|
||||||
(particle::build, "particle.rs"),
|
(particle::build, "particle.rs"),
|
||||||
|
|||||||
@ -47,10 +47,12 @@ default = [
|
|||||||
"villager",
|
"villager",
|
||||||
"placed_feature",
|
"placed_feature",
|
||||||
"configured_feature",
|
"configured_feature",
|
||||||
"slot_ranges"
|
"slot_ranges",
|
||||||
|
"bedrock_creative"
|
||||||
]
|
]
|
||||||
|
|
||||||
advancement = []
|
advancement = []
|
||||||
|
bedrock_creative = []
|
||||||
item = []
|
item = []
|
||||||
packet = []
|
packet = []
|
||||||
jukebox_song = []
|
jukebox_song = []
|
||||||
|
|||||||
10121
pumpkin-data/src/generated/bedrock_creative.rs
Normal file
10121
pumpkin-data/src/generated/bedrock_creative.rs
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -250,6 +250,11 @@ pub mod entity_id_remap;
|
|||||||
#[path = "generated/sound_id_remap.rs"]
|
#[path = "generated/sound_id_remap.rs"]
|
||||||
pub mod sound_id_remap;
|
pub mod sound_id_remap;
|
||||||
|
|
||||||
|
#[cfg(feature = "bedrock_creative")]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[path = "generated/bedrock_creative.rs"]
|
||||||
|
pub mod bedrock_creative;
|
||||||
|
|
||||||
#[cfg(feature = "tag")]
|
#[cfg(feature = "tag")]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[path = "generated/tag.rs"]
|
#[path = "generated/tag.rs"]
|
||||||
|
|||||||
@ -13,7 +13,7 @@ query = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pumpkin-nbt.workspace = true
|
pumpkin-nbt.workspace = true
|
||||||
pumpkin-data = { workspace = true, features = ["packet", "item_id_remap", "entity_id_remap", "sound_id_remap", "recipes"] }
|
pumpkin-data = { workspace = true, features = ["packet", "item_id_remap", "entity_id_remap", "sound_id_remap", "recipes", "bedrock_creative"] }
|
||||||
pumpkin-macros.workspace = true
|
pumpkin-macros.workspace = true
|
||||||
pumpkin-world.workspace = true
|
pumpkin-world.workspace = true
|
||||||
pumpkin-util.workspace = true
|
pumpkin-util.workspace = true
|
||||||
|
|||||||
62
pumpkin-protocol/src/bedrock/client/item_stack_response.rs
Normal file
62
pumpkin-protocol/src/bedrock/client/item_stack_response.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use std::io::{Error, Write};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bedrock::network_item::FullContainerName,
|
||||||
|
codec::{var_int::VarInt, var_uint::VarUInt},
|
||||||
|
serial::PacketWrite,
|
||||||
|
};
|
||||||
|
use pumpkin_macros::packet;
|
||||||
|
|
||||||
|
#[derive(PacketWrite, Debug, Clone)]
|
||||||
|
pub struct ItemStackResponseSlotInfo {
|
||||||
|
pub slot: u8,
|
||||||
|
pub hotbar_slot: u8,
|
||||||
|
pub count: u8,
|
||||||
|
pub item_stack_id: VarInt,
|
||||||
|
pub custom_name: String,
|
||||||
|
pub filtered_custom_name: String,
|
||||||
|
pub durability_correction: VarInt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PacketWrite, Debug, Clone)]
|
||||||
|
pub struct ItemStackResponseContainerInfo {
|
||||||
|
pub container_name: FullContainerName,
|
||||||
|
pub slots: Vec<ItemStackResponseSlotInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ItemStackResponse {
|
||||||
|
pub result: u8, // 0 = SUCCESS, 1 = ERROR
|
||||||
|
pub request_id: VarInt,
|
||||||
|
pub container_infos: Vec<ItemStackResponseContainerInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketWrite for ItemStackResponse {
|
||||||
|
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||||
|
self.result.write(writer)?;
|
||||||
|
self.request_id.write(writer)?;
|
||||||
|
if self.result == 0 {
|
||||||
|
VarUInt(self.container_infos.len() as u32).write(writer)?;
|
||||||
|
for info in &self.container_infos {
|
||||||
|
info.write(writer)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[packet(148)]
|
||||||
|
pub struct CItemStackResponse {
|
||||||
|
pub responses: Vec<ItemStackResponse>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketWrite for CItemStackResponse {
|
||||||
|
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||||
|
VarUInt(self.responses.len() as u32).write(writer)?;
|
||||||
|
for response in &self.responses {
|
||||||
|
response.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,6 +13,7 @@ pub mod gamerules_changed;
|
|||||||
pub mod handshake;
|
pub mod handshake;
|
||||||
pub mod inventory_content;
|
pub mod inventory_content;
|
||||||
pub mod item_registry;
|
pub mod item_registry;
|
||||||
|
pub mod item_stack_response;
|
||||||
pub mod level_chunk;
|
pub mod level_chunk;
|
||||||
pub mod level_event;
|
pub mod level_event;
|
||||||
pub mod modal_form_request;
|
pub mod modal_form_request;
|
||||||
@ -56,6 +57,8 @@ pub use disconnect_player::*;
|
|||||||
pub use gamerules_changed::*;
|
pub use gamerules_changed::*;
|
||||||
pub use handshake::*;
|
pub use handshake::*;
|
||||||
pub use inventory_content::*;
|
pub use inventory_content::*;
|
||||||
|
pub use item_registry::*;
|
||||||
|
pub use item_stack_response::*;
|
||||||
pub use level_chunk::*;
|
pub use level_chunk::*;
|
||||||
pub use level_event::*;
|
pub use level_event::*;
|
||||||
pub use modal_form_request::*;
|
pub use modal_form_request::*;
|
||||||
|
|||||||
@ -115,7 +115,7 @@ impl From<&ItemStack> for NetworkItemDescriptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct NetworkItemStackDescriptor {
|
pub struct NetworkItemStackDescriptor {
|
||||||
// I hate mojang
|
// I hate mojang
|
||||||
// https://mojang.github.io/bedrock-protocol-docs/html/cerealizer_NetworkItemStackDescriptor___SerializedData.html
|
// https://mojang.github.io/bedrock-protocol-docs/html/cerealizer_NetworkItemStackDescriptor___SerializedData.html
|
||||||
@ -130,6 +130,13 @@ impl PacketWrite for NetworkItemStackDescriptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PacketRead for NetworkItemStackDescriptor {
|
||||||
|
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let item = NetworkItemDescriptor::read(buf)?;
|
||||||
|
Ok(Self { item, net_id: None })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&ItemStack> for NetworkItemStackDescriptor {
|
impl From<&ItemStack> for NetworkItemStackDescriptor {
|
||||||
fn from(stack: &ItemStack) -> Self {
|
fn from(stack: &ItemStack) -> Self {
|
||||||
if stack.is_empty() {
|
if stack.is_empty() {
|
||||||
@ -155,13 +162,13 @@ impl From<&ItemStack> for NetworkItemStackDescriptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PacketWrite, Clone)]
|
#[derive(PacketWrite, PacketRead, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct FullContainerName {
|
pub struct FullContainerName {
|
||||||
pub container_name: ContainerName,
|
pub container_name: ContainerName,
|
||||||
pub dynamic_id: Option<u32>,
|
pub dynamic_id: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ContainerName {
|
pub enum ContainerName {
|
||||||
AnvilInput,
|
AnvilInput,
|
||||||
@ -233,9 +240,135 @@ pub enum ContainerName {
|
|||||||
RecipeFurnaceItems,
|
RecipeFurnaceItems,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u8> for ContainerName {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
0 => Ok(Self::AnvilInput),
|
||||||
|
1 => Ok(Self::AnvilMaterial),
|
||||||
|
2 => Ok(Self::AnvilResultPreview),
|
||||||
|
3 => Ok(Self::SmithingTableInput),
|
||||||
|
4 => Ok(Self::SmithingTableMaterial),
|
||||||
|
5 => Ok(Self::SmithingTableResultPreview),
|
||||||
|
6 => Ok(Self::Armor),
|
||||||
|
7 => Ok(Self::LevelEntity),
|
||||||
|
8 => Ok(Self::BeaconPayment),
|
||||||
|
9 => Ok(Self::BrewingStandInput),
|
||||||
|
10 => Ok(Self::BrewingStandResult),
|
||||||
|
11 => Ok(Self::BrewingStandFuel),
|
||||||
|
12 => Ok(Self::CombinedHotBarAndInventory),
|
||||||
|
13 => Ok(Self::CraftingInput),
|
||||||
|
14 => Ok(Self::CraftingOutputPreview),
|
||||||
|
15 => Ok(Self::RecipeConstruction),
|
||||||
|
16 => Ok(Self::RecipeNature),
|
||||||
|
17 => Ok(Self::RecipeItems),
|
||||||
|
18 => Ok(Self::RecipeSearch),
|
||||||
|
19 => Ok(Self::RecipeSearchBar),
|
||||||
|
20 => Ok(Self::RecipeEquipment),
|
||||||
|
21 => Ok(Self::RecipeBook),
|
||||||
|
22 => Ok(Self::EnchantingInput),
|
||||||
|
23 => Ok(Self::EnchantingMaterial),
|
||||||
|
24 => Ok(Self::FurnaceFuel),
|
||||||
|
25 => Ok(Self::FurnaceIngredient),
|
||||||
|
26 => Ok(Self::FurnaceResult),
|
||||||
|
27 => Ok(Self::HorseEquip),
|
||||||
|
28 => Ok(Self::HotBar),
|
||||||
|
29 => Ok(Self::Inventory),
|
||||||
|
30 => Ok(Self::ShulkerBox),
|
||||||
|
31 => Ok(Self::TradeIngredient1),
|
||||||
|
32 => Ok(Self::TradeIngredient2),
|
||||||
|
33 => Ok(Self::TradeResultPreview),
|
||||||
|
34 => Ok(Self::Offhand),
|
||||||
|
35 => Ok(Self::CompoundCreatorInput),
|
||||||
|
36 => Ok(Self::CompoundCreatorOutputPreview),
|
||||||
|
37 => Ok(Self::ElementConstructorOutputPreview),
|
||||||
|
38 => Ok(Self::MaterialReducerInput),
|
||||||
|
39 => Ok(Self::MaterialReducerOutput),
|
||||||
|
40 => Ok(Self::LabTableInput),
|
||||||
|
41 => Ok(Self::LoomInput),
|
||||||
|
42 => Ok(Self::LoomDye),
|
||||||
|
43 => Ok(Self::LoomMaterial),
|
||||||
|
44 => Ok(Self::LoomResultPreview),
|
||||||
|
45 => Ok(Self::BlastFurnaceIngredient),
|
||||||
|
46 => Ok(Self::SmokerIngredient),
|
||||||
|
47 => Ok(Self::Trade2Ingredient1),
|
||||||
|
48 => Ok(Self::Trade2Ingredient2),
|
||||||
|
49 => Ok(Self::Trade2ResultPreview),
|
||||||
|
50 => Ok(Self::GrindstoneInput),
|
||||||
|
51 => Ok(Self::GrindstoneAdditional),
|
||||||
|
52 => Ok(Self::GrindstoneResultPreview),
|
||||||
|
53 => Ok(Self::StonecutterInput),
|
||||||
|
54 => Ok(Self::StonecutterResultPreview),
|
||||||
|
55 => Ok(Self::CartographyInput),
|
||||||
|
56 => Ok(Self::CartographyAdditional),
|
||||||
|
57 => Ok(Self::CartographyResultPreview),
|
||||||
|
58 => Ok(Self::Barrel),
|
||||||
|
59 => Ok(Self::Cursor),
|
||||||
|
60 => Ok(Self::CreatedOutput),
|
||||||
|
61 => Ok(Self::SmithingTableTemplate),
|
||||||
|
62 => Ok(Self::CrafterLevelEntity),
|
||||||
|
63 => Ok(Self::Dynamic),
|
||||||
|
64 => Ok(Self::RecipeFood),
|
||||||
|
65 => Ok(Self::RecipeBlocks),
|
||||||
|
66 => Ok(Self::RecipeFurnaceItems),
|
||||||
|
_ => Err(Error::new(
|
||||||
|
std::io::ErrorKind::InvalidData,
|
||||||
|
format!("Invalid ContainerName ID: {value}"),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PacketWrite for ContainerName {
|
impl PacketWrite for ContainerName {
|
||||||
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||||
(*self as u8).write(writer)?;
|
(*self as u8).write(writer)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PacketRead for ContainerName {
|
||||||
|
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let value = u8::read(buf)?;
|
||||||
|
Self::try_from(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NetworkItemStack {
|
||||||
|
pub id: VarInt,
|
||||||
|
pub count: u16,
|
||||||
|
pub aux_value: VarUInt,
|
||||||
|
pub block_runtime_id: VarInt,
|
||||||
|
pub extra_data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketRead for NetworkItemStack {
|
||||||
|
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let id = VarInt::read(buf)?;
|
||||||
|
if id.0 == 0 {
|
||||||
|
return Ok(Self {
|
||||||
|
id,
|
||||||
|
count: 0,
|
||||||
|
aux_value: VarUInt(0),
|
||||||
|
block_runtime_id: VarInt(0),
|
||||||
|
extra_data: Vec::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let count = u16::read(buf)?;
|
||||||
|
let aux_value = VarUInt::read(buf)?;
|
||||||
|
let block_runtime_id = VarInt::read(buf)?;
|
||||||
|
|
||||||
|
let extra_data_len = VarUInt::read(buf)?.0;
|
||||||
|
let mut extra_data = vec![0u8; extra_data_len as usize];
|
||||||
|
buf.read_exact(&mut extra_data)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
id,
|
||||||
|
count,
|
||||||
|
aux_value,
|
||||||
|
block_runtime_id,
|
||||||
|
extra_data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
12
pumpkin-protocol/src/bedrock/server/block_pick_request.rs
Normal file
12
pumpkin-protocol/src/bedrock/server/block_pick_request.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use pumpkin_macros::packet;
|
||||||
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
|
||||||
|
use crate::serial::PacketRead;
|
||||||
|
|
||||||
|
#[derive(Debug, PacketRead)]
|
||||||
|
#[packet(34)]
|
||||||
|
pub struct SBlockPickRequest {
|
||||||
|
pub block_pos: BlockPos,
|
||||||
|
pub add_block_nbt: bool,
|
||||||
|
pub hotbar_slot: u8,
|
||||||
|
}
|
||||||
285
pumpkin-protocol/src/bedrock/server/item_stack_request.rs
Normal file
285
pumpkin-protocol/src/bedrock/server/item_stack_request.rs
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
use std::io::{Error, ErrorKind, Read, Write};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bedrock::network_item::FullContainerName,
|
||||||
|
codec::{var_int::VarInt, var_uint::VarUInt},
|
||||||
|
serial::{PacketRead, PacketWrite},
|
||||||
|
};
|
||||||
|
use pumpkin_macros::packet;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ItemStackRequestSlotInfo {
|
||||||
|
pub container_name: FullContainerName,
|
||||||
|
pub slot_id: u8,
|
||||||
|
pub stack_id: VarInt,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketRead for ItemStackRequestSlotInfo {
|
||||||
|
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let container_name = FullContainerName::read(buf)?;
|
||||||
|
let slot_id = u8::read(buf)?;
|
||||||
|
let stack_id = VarInt::read(buf)?;
|
||||||
|
Ok(Self {
|
||||||
|
container_name,
|
||||||
|
slot_id,
|
||||||
|
stack_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketWrite for ItemStackRequestSlotInfo {
|
||||||
|
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||||
|
self.container_name.write(writer)?;
|
||||||
|
self.slot_id.write(writer)?;
|
||||||
|
self.stack_id.write(writer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ItemStackRequestAction {
|
||||||
|
Take {
|
||||||
|
count: u8,
|
||||||
|
source: ItemStackRequestSlotInfo,
|
||||||
|
destination: ItemStackRequestSlotInfo,
|
||||||
|
},
|
||||||
|
Place {
|
||||||
|
count: u8,
|
||||||
|
source: ItemStackRequestSlotInfo,
|
||||||
|
destination: ItemStackRequestSlotInfo,
|
||||||
|
},
|
||||||
|
Swap {
|
||||||
|
slot1: ItemStackRequestSlotInfo,
|
||||||
|
slot2: ItemStackRequestSlotInfo,
|
||||||
|
},
|
||||||
|
Drop {
|
||||||
|
count: u8,
|
||||||
|
source: ItemStackRequestSlotInfo,
|
||||||
|
randomly: bool,
|
||||||
|
},
|
||||||
|
Destroy {
|
||||||
|
count: u8,
|
||||||
|
source: ItemStackRequestSlotInfo,
|
||||||
|
},
|
||||||
|
Consume {
|
||||||
|
count: u8,
|
||||||
|
source: ItemStackRequestSlotInfo,
|
||||||
|
},
|
||||||
|
Create {
|
||||||
|
result_index: u8,
|
||||||
|
},
|
||||||
|
PlaceInContainer {
|
||||||
|
count: u8,
|
||||||
|
source: ItemStackRequestSlotInfo,
|
||||||
|
destination: ItemStackRequestSlotInfo,
|
||||||
|
},
|
||||||
|
TakeOutContainer {
|
||||||
|
count: u8,
|
||||||
|
source: ItemStackRequestSlotInfo,
|
||||||
|
destination: ItemStackRequestSlotInfo,
|
||||||
|
},
|
||||||
|
LabTableCombine,
|
||||||
|
BeaconPayment {
|
||||||
|
primary_effect_id: VarInt,
|
||||||
|
secondary_effect_id: VarInt,
|
||||||
|
},
|
||||||
|
MineBlock {
|
||||||
|
hotbar_slot: VarInt,
|
||||||
|
predicted_durability: VarInt,
|
||||||
|
stack_id: VarInt,
|
||||||
|
},
|
||||||
|
CraftRecipe {
|
||||||
|
recipe_id: VarUInt,
|
||||||
|
repetitions: u8,
|
||||||
|
},
|
||||||
|
CraftRecipeAuto {
|
||||||
|
recipe_id: VarUInt,
|
||||||
|
repetitions: u8,
|
||||||
|
repetitions2: u8,
|
||||||
|
},
|
||||||
|
CraftCreative {
|
||||||
|
creative_item_id: VarUInt,
|
||||||
|
repetitions: u8,
|
||||||
|
},
|
||||||
|
Optional {
|
||||||
|
recipe_id: VarUInt,
|
||||||
|
filter_string_index: i32,
|
||||||
|
},
|
||||||
|
Grindstone {
|
||||||
|
recipe_id: VarUInt,
|
||||||
|
repair_cost: VarInt,
|
||||||
|
repetitions: u8,
|
||||||
|
},
|
||||||
|
Loom {
|
||||||
|
pattern_id: String,
|
||||||
|
repetitions: u8,
|
||||||
|
},
|
||||||
|
CraftNonImplemented,
|
||||||
|
CraftResultsDeprecated {
|
||||||
|
result_items: Vec<crate::bedrock::network_item::NetworkItemStack>,
|
||||||
|
times_crafted: u8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketRead for ItemStackRequestAction {
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let action_type = u8::read(buf)?;
|
||||||
|
match action_type {
|
||||||
|
0 => Ok(Self::Take {
|
||||||
|
count: u8::read(buf)?,
|
||||||
|
source: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
destination: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
}),
|
||||||
|
1 => Ok(Self::Place {
|
||||||
|
count: u8::read(buf)?,
|
||||||
|
source: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
destination: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
}),
|
||||||
|
2 => Ok(Self::Swap {
|
||||||
|
slot1: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
slot2: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
}),
|
||||||
|
3 => Ok(Self::Drop {
|
||||||
|
count: u8::read(buf)?,
|
||||||
|
source: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
randomly: bool::read(buf)?,
|
||||||
|
}),
|
||||||
|
4 => Ok(Self::Destroy {
|
||||||
|
count: u8::read(buf)?,
|
||||||
|
source: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
}),
|
||||||
|
5 => Ok(Self::Consume {
|
||||||
|
count: u8::read(buf)?,
|
||||||
|
source: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
}),
|
||||||
|
6 => Ok(Self::Create {
|
||||||
|
result_index: u8::read(buf)?,
|
||||||
|
}),
|
||||||
|
7 => Ok(Self::PlaceInContainer {
|
||||||
|
count: u8::read(buf)?,
|
||||||
|
source: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
destination: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
}),
|
||||||
|
8 => Ok(Self::TakeOutContainer {
|
||||||
|
count: u8::read(buf)?,
|
||||||
|
source: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
destination: ItemStackRequestSlotInfo::read(buf)?,
|
||||||
|
}),
|
||||||
|
9 => Ok(Self::LabTableCombine),
|
||||||
|
10 => Ok(Self::BeaconPayment {
|
||||||
|
primary_effect_id: VarInt::read(buf)?,
|
||||||
|
secondary_effect_id: VarInt::read(buf)?,
|
||||||
|
}),
|
||||||
|
11 => Ok(Self::MineBlock {
|
||||||
|
hotbar_slot: VarInt::read(buf)?,
|
||||||
|
predicted_durability: VarInt::read(buf)?,
|
||||||
|
stack_id: VarInt::read(buf)?,
|
||||||
|
}),
|
||||||
|
12 => Ok(Self::CraftRecipe {
|
||||||
|
recipe_id: VarUInt::read(buf)?,
|
||||||
|
repetitions: u8::read(buf)?,
|
||||||
|
}),
|
||||||
|
13 => {
|
||||||
|
let recipe_id = VarUInt::read(buf)?;
|
||||||
|
let repetitions = u8::read(buf)?;
|
||||||
|
let repetitions2 = u8::read(buf)?;
|
||||||
|
let count = u8::read(buf)?;
|
||||||
|
// Skip ingredients
|
||||||
|
if count > 0 {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::InvalidData,
|
||||||
|
"CraftRecipeAuto stack action is unimplemented",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(Self::CraftRecipeAuto {
|
||||||
|
recipe_id,
|
||||||
|
repetitions,
|
||||||
|
repetitions2,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
14 => Ok(Self::CraftCreative {
|
||||||
|
creative_item_id: VarUInt::read(buf)?,
|
||||||
|
repetitions: u8::read(buf)?,
|
||||||
|
}),
|
||||||
|
15 => Ok(Self::Optional {
|
||||||
|
recipe_id: VarUInt::read(buf)?,
|
||||||
|
filter_string_index: i32::read(buf)?,
|
||||||
|
}),
|
||||||
|
16 => Ok(Self::Grindstone {
|
||||||
|
recipe_id: VarUInt::read(buf)?,
|
||||||
|
repair_cost: VarInt::read(buf)?,
|
||||||
|
repetitions: u8::read(buf)?,
|
||||||
|
}),
|
||||||
|
17 => Ok(Self::Loom {
|
||||||
|
pattern_id: String::read(buf)?,
|
||||||
|
repetitions: u8::read(buf)?,
|
||||||
|
}),
|
||||||
|
18 => Ok(Self::CraftNonImplemented),
|
||||||
|
19 => {
|
||||||
|
let result_items_len = VarUInt::read(buf)?.0;
|
||||||
|
let mut result_items = Vec::with_capacity(result_items_len as usize);
|
||||||
|
for _ in 0..result_items_len {
|
||||||
|
result_items.push(crate::bedrock::network_item::NetworkItemStack::read(buf)?);
|
||||||
|
}
|
||||||
|
let times_crafted = u8::read(buf)?;
|
||||||
|
Ok(Self::CraftResultsDeprecated {
|
||||||
|
result_items,
|
||||||
|
times_crafted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(Error::new(
|
||||||
|
ErrorKind::InvalidData,
|
||||||
|
format!("Unknown ItemStackRequestAction ID: {action_type}"),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ItemStackRequest {
|
||||||
|
pub request_id: VarInt,
|
||||||
|
pub actions: Vec<ItemStackRequestAction>,
|
||||||
|
pub filter_strings: Vec<String>,
|
||||||
|
pub filter_cause: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketRead for ItemStackRequest {
|
||||||
|
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let request_id = VarInt::read(buf)?;
|
||||||
|
let actions_len = VarUInt::read(buf)?.0;
|
||||||
|
let mut actions = Vec::with_capacity(actions_len as usize);
|
||||||
|
for _ in 0..actions_len {
|
||||||
|
actions.push(ItemStackRequestAction::read(buf)?);
|
||||||
|
}
|
||||||
|
let filter_strings_len = VarUInt::read(buf)?.0;
|
||||||
|
let mut filter_strings = Vec::with_capacity(filter_strings_len as usize);
|
||||||
|
for _ in 0..filter_strings_len {
|
||||||
|
filter_strings.push(String::read(buf)?);
|
||||||
|
}
|
||||||
|
let filter_cause = i32::read(buf)?;
|
||||||
|
Ok(Self {
|
||||||
|
request_id,
|
||||||
|
actions,
|
||||||
|
filter_strings,
|
||||||
|
filter_cause,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[packet(147)]
|
||||||
|
pub struct SItemStackRequest {
|
||||||
|
pub requests: Vec<ItemStackRequest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketRead for SItemStackRequest {
|
||||||
|
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
|
||||||
|
let requests_len = VarUInt::read(buf)?.0;
|
||||||
|
let mut requests = Vec::with_capacity(requests_len as usize);
|
||||||
|
for _ in 0..requests_len {
|
||||||
|
requests.push(ItemStackRequest::read(buf)?);
|
||||||
|
}
|
||||||
|
Ok(Self { requests })
|
||||||
|
}
|
||||||
|
}
|
||||||
42
pumpkin-protocol/src/bedrock/server/mob_equipment.rs
Normal file
42
pumpkin-protocol/src/bedrock/server/mob_equipment.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use crate::{
|
||||||
|
bedrock::network_item::NetworkItemDescriptor,
|
||||||
|
codec::{var_int::VarInt, var_ulong::VarULong},
|
||||||
|
serial::PacketRead,
|
||||||
|
};
|
||||||
|
use pumpkin_macros::packet;
|
||||||
|
use std::io::{Error, Read};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[packet(31)]
|
||||||
|
pub struct SMobEquipment {
|
||||||
|
pub entity_runtime_id: VarULong,
|
||||||
|
pub stack_id: VarInt,
|
||||||
|
pub item: Option<NetworkItemDescriptor>,
|
||||||
|
pub inventory_slot: u8,
|
||||||
|
pub hotbar_slot: u8,
|
||||||
|
pub window_id: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketRead for SMobEquipment {
|
||||||
|
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
|
||||||
|
let entity_runtime_id = VarULong::read(reader)?;
|
||||||
|
let stack_id = VarInt::read(reader)?;
|
||||||
|
let item = if stack_id.0 == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(NetworkItemDescriptor::read(reader)?)
|
||||||
|
};
|
||||||
|
let inventory_slot = u8::read(reader)?;
|
||||||
|
let hotbar_slot = u8::read(reader)?;
|
||||||
|
let window_id = u8::read(reader)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
entity_runtime_id,
|
||||||
|
stack_id,
|
||||||
|
item,
|
||||||
|
inventory_slot,
|
||||||
|
hotbar_slot,
|
||||||
|
window_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
pub mod actor_event;
|
pub mod actor_event;
|
||||||
pub mod animate;
|
pub mod animate;
|
||||||
|
pub mod block_pick_request;
|
||||||
pub mod client_cache_status;
|
pub mod client_cache_status;
|
||||||
pub mod command_request;
|
pub mod command_request;
|
||||||
pub mod container_close;
|
pub mod container_close;
|
||||||
@ -7,8 +8,10 @@ pub mod emote;
|
|||||||
pub mod emote_list;
|
pub mod emote_list;
|
||||||
pub mod interaction;
|
pub mod interaction;
|
||||||
pub mod inventory_transaction;
|
pub mod inventory_transaction;
|
||||||
|
pub mod item_stack_request;
|
||||||
pub mod loading_screen;
|
pub mod loading_screen;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
|
pub mod mob_equipment;
|
||||||
pub mod modal_form_response;
|
pub mod modal_form_response;
|
||||||
pub mod player_action;
|
pub mod player_action;
|
||||||
pub mod player_auth_input;
|
pub mod player_auth_input;
|
||||||
@ -21,6 +24,7 @@ pub mod text;
|
|||||||
|
|
||||||
pub use actor_event::*;
|
pub use actor_event::*;
|
||||||
pub use animate::*;
|
pub use animate::*;
|
||||||
|
pub use block_pick_request::*;
|
||||||
pub use client_cache_status::*;
|
pub use client_cache_status::*;
|
||||||
pub use command_request::*;
|
pub use command_request::*;
|
||||||
pub use container_close::*;
|
pub use container_close::*;
|
||||||
@ -28,8 +32,10 @@ pub use emote::*;
|
|||||||
pub use emote_list::*;
|
pub use emote_list::*;
|
||||||
pub use interaction::{Action as InteractAction, SInteraction};
|
pub use interaction::{Action as InteractAction, SInteraction};
|
||||||
pub use inventory_transaction::*;
|
pub use inventory_transaction::*;
|
||||||
|
pub use item_stack_request::*;
|
||||||
pub use loading_screen::*;
|
pub use loading_screen::*;
|
||||||
pub use login::*;
|
pub use login::*;
|
||||||
|
pub use mob_equipment::*;
|
||||||
pub use modal_form_response::*;
|
pub use modal_form_response::*;
|
||||||
pub use player_action::{Action as PlayerActionType, SPlayerAction};
|
pub use player_action::{Action as PlayerActionType, SPlayerAction};
|
||||||
pub use player_auth_input::*;
|
pub use player_auth_input::*;
|
||||||
|
|||||||
@ -2488,7 +2488,7 @@ impl Entity {
|
|||||||
MetadataValue::Long(self.bedrock_flags_two.load(Ordering::Relaxed)),
|
MetadataValue::Long(self.bedrock_flags_two.load(Ordering::Relaxed)),
|
||||||
);
|
);
|
||||||
client
|
client
|
||||||
.send_game_packet(&CSetActorData {
|
.enqueue_packet(&CSetActorData {
|
||||||
actor_runtime_id: VarULong(self.entity_id as u64),
|
actor_runtime_id: VarULong(self.entity_id as u64),
|
||||||
metadata,
|
metadata,
|
||||||
synced_properties: PropertySyncData {
|
synced_properties: PropertySyncData {
|
||||||
|
|||||||
@ -3400,16 +3400,40 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn close_handled_screen(self: &Arc<Self>) {
|
pub async fn close_handled_screen(self: &Arc<Self>) {
|
||||||
|
let (sync_id, bedrock_window_type) = {
|
||||||
|
let current_handler_guard = self.current_screen_handler.lock().await;
|
||||||
|
let handler = current_handler_guard.lock().await;
|
||||||
|
let sync_id = handler.sync_id();
|
||||||
|
let window_type = handler.window_type();
|
||||||
|
let bedrock_window_type = match window_type {
|
||||||
|
Some(WindowType::Crafting) => 1,
|
||||||
|
Some(WindowType::Furnace) => 2,
|
||||||
|
Some(WindowType::Enchantment) => 3,
|
||||||
|
Some(WindowType::BrewingStand) => 4,
|
||||||
|
Some(WindowType::Anvil) => 5,
|
||||||
|
Some(WindowType::Hopper) => 8,
|
||||||
|
Some(WindowType::Beacon) => 13,
|
||||||
|
Some(WindowType::BlastFurnace) => 27,
|
||||||
|
Some(WindowType::Smoker) => 28,
|
||||||
|
Some(WindowType::Stonecutter) => 29,
|
||||||
|
Some(WindowType::CartographyTable) => 30,
|
||||||
|
Some(WindowType::Grindstone) => 26,
|
||||||
|
Some(WindowType::Loom) => 24,
|
||||||
|
Some(WindowType::Smithing) => 34,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
(sync_id, bedrock_window_type)
|
||||||
|
};
|
||||||
|
|
||||||
self.client
|
self.client
|
||||||
.enqueue_packet(&CCloseContainer::new(
|
.enqueue_packet_editioned(
|
||||||
self.current_screen_handler
|
&CCloseContainer::new(sync_id.into()),
|
||||||
.lock()
|
&pumpkin_protocol::bedrock::server::container_close::SContainerClose {
|
||||||
.await
|
container_id: sync_id,
|
||||||
.lock()
|
container_type: bedrock_window_type,
|
||||||
.await
|
server_initiated: true,
|
||||||
.sync_id()
|
},
|
||||||
.into(),
|
)
|
||||||
))
|
|
||||||
.await;
|
.await;
|
||||||
self.on_handled_screen_closed().await;
|
self.on_handled_screen_closed().await;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -504,6 +504,13 @@ impl PumpkinServer {
|
|||||||
let be_clients = bedrock_clients.clone();
|
let be_clients = bedrock_clients.clone();
|
||||||
let mut clients_guard = bedrock_clients.lock().await;
|
let mut clients_guard = bedrock_clients.lock().await;
|
||||||
|
|
||||||
|
if clients_guard
|
||||||
|
.get(&client_addr)
|
||||||
|
.is_some_and(|client| client.is_closed())
|
||||||
|
{
|
||||||
|
clients_guard.remove(&client_addr);
|
||||||
|
}
|
||||||
|
|
||||||
let mut is_new = false;
|
let mut is_new = false;
|
||||||
let client = clients_guard.entry(client_addr).or_insert_with(|| {
|
let client = clients_guard.entry(client_addr).or_insert_with(|| {
|
||||||
is_new = true;
|
is_new = true;
|
||||||
@ -528,6 +535,7 @@ impl PumpkinServer {
|
|||||||
match login_result {
|
match login_result {
|
||||||
PacketHandlerResult::Stop => {
|
PacketHandlerResult::Stop => {
|
||||||
client_clone.close().await;
|
client_clone.close().await;
|
||||||
|
client_clone.await_tasks().await;
|
||||||
}
|
}
|
||||||
PacketHandlerResult::ReadyToPlay(profile, config) => {
|
PacketHandlerResult::ReadyToPlay(profile, config) => {
|
||||||
if let Some((player, _world)) = server_clone
|
if let Some((player, _world)) = server_clone
|
||||||
@ -539,6 +547,7 @@ impl PumpkinServer {
|
|||||||
client_clone.progress_player_packets(&player, &server_clone).await;
|
client_clone.progress_player_packets(&player, &server_clone).await;
|
||||||
|
|
||||||
client_clone.close().await;
|
client_clone.close().await;
|
||||||
|
client_clone.await_tasks().await;
|
||||||
player.remove().await;
|
player.remove().await;
|
||||||
server_clone.remove_player(&player).await;
|
server_clone.remove_player(&player).await;
|
||||||
if let Err(e) = server_clone.player_data_storage
|
if let Err(e) = server_clone.player_data_storage
|
||||||
@ -564,7 +573,8 @@ impl PumpkinServer {
|
|||||||
id,
|
id,
|
||||||
&mut Cursor::new(&udp_buf[1..len]),
|
&mut Cursor::new(&udp_buf[1..len]),
|
||||||
client_addr,
|
client_addr,
|
||||||
sock
|
sock,
|
||||||
|
bedrock_clients,
|
||||||
).await;
|
).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,7 @@ use pumpkin_protocol::{
|
|||||||
packet_encoder::UDPNetworkEncoder,
|
packet_encoder::UDPNetworkEncoder,
|
||||||
server::{
|
server::{
|
||||||
animate::SAnimate,
|
animate::SAnimate,
|
||||||
|
block_pick_request::SBlockPickRequest,
|
||||||
client_cache_status::SClientCacheStatus,
|
client_cache_status::SClientCacheStatus,
|
||||||
command_request::SCommandRequest,
|
command_request::SCommandRequest,
|
||||||
container_close::SContainerClose,
|
container_close::SContainerClose,
|
||||||
@ -38,6 +39,7 @@ use pumpkin_protocol::{
|
|||||||
inventory_transaction::SInventoryTransaction,
|
inventory_transaction::SInventoryTransaction,
|
||||||
loading_screen::SLoadingScreen,
|
loading_screen::SLoadingScreen,
|
||||||
login::SLogin,
|
login::SLogin,
|
||||||
|
mob_equipment::SMobEquipment,
|
||||||
player_action::SPlayerAction,
|
player_action::SPlayerAction,
|
||||||
player_auth_input::SPlayerAuthInput,
|
player_auth_input::SPlayerAuthInput,
|
||||||
raknet::{
|
raknet::{
|
||||||
@ -136,6 +138,7 @@ pub struct BedrockClient {
|
|||||||
output_ordered_index: AtomicU32,
|
output_ordered_index: AtomicU32,
|
||||||
/// The next form ID to use for custom forms.
|
/// The next form ID to use for custom forms.
|
||||||
pub next_form_id: AtomicU32,
|
pub next_form_id: AtomicU32,
|
||||||
|
pub inventory_opened: AtomicBool,
|
||||||
/// An notifier that is triggered when this client is closed.
|
/// An notifier that is triggered when this client is closed.
|
||||||
close_token: CancellationToken,
|
close_token: CancellationToken,
|
||||||
last_seen: Arc<AtomicCell<std::time::Instant>>,
|
last_seen: Arc<AtomicCell<std::time::Instant>>,
|
||||||
@ -184,6 +187,7 @@ impl BedrockClient {
|
|||||||
output_sequenced_index: AtomicU32::new(0),
|
output_sequenced_index: AtomicU32::new(0),
|
||||||
output_ordered_index: AtomicU32::new(0),
|
output_ordered_index: AtomicU32::new(0),
|
||||||
next_form_id: AtomicU32::new(0),
|
next_form_id: AtomicU32::new(0),
|
||||||
|
inventory_opened: AtomicBool::new(false),
|
||||||
compounds: Arc::new(Mutex::new(HashMap::new())),
|
compounds: Arc::new(Mutex::new(HashMap::new())),
|
||||||
close_token: CancellationToken::new(),
|
close_token: CancellationToken::new(),
|
||||||
last_seen: Arc::new(AtomicCell::new(std::time::Instant::now())),
|
last_seen: Arc::new(AtomicCell::new(std::time::Instant::now())),
|
||||||
@ -650,12 +654,11 @@ impl BedrockClient {
|
|||||||
}
|
}
|
||||||
self.close_token.cancel();
|
self.close_token.cancel();
|
||||||
self.be_clients.lock().await.remove(&self.address);
|
self.be_clients.lock().await.remove(&self.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn await_tasks(&self) {
|
||||||
self.tasks.close();
|
self.tasks.close();
|
||||||
self.tasks.wait().await;
|
self.tasks.wait().await;
|
||||||
|
|
||||||
if let Some(player) = self.player.lock().await.as_ref() {
|
|
||||||
player.remove().await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_closed(&self) -> bool {
|
pub fn is_closed(&self) -> bool {
|
||||||
@ -1011,6 +1014,9 @@ impl BedrockClient {
|
|||||||
SInventoryTransaction::PACKET_ID => {
|
SInventoryTransaction::PACKET_ID => {
|
||||||
self.handle_inventory_action(player, SInventoryTransaction::read(reader)?).await;
|
self.handle_inventory_action(player, SInventoryTransaction::read(reader)?).await;
|
||||||
}
|
}
|
||||||
|
pumpkin_protocol::bedrock::server::item_stack_request::SItemStackRequest::PACKET_ID => {
|
||||||
|
self.handle_item_stack_request(player, pumpkin_protocol::bedrock::server::item_stack_request::SItemStackRequest::read(reader)?).await;
|
||||||
|
}
|
||||||
SInteraction::PACKET_ID => {
|
SInteraction::PACKET_ID => {
|
||||||
self.handle_interaction(player, SInteraction::read(reader)?)
|
self.handle_interaction(player, SInteraction::read(reader)?)
|
||||||
.await;
|
.await;
|
||||||
@ -1059,6 +1065,14 @@ impl BedrockClient {
|
|||||||
SLoadingScreen::PACKET_ID => {
|
SLoadingScreen::PACKET_ID => {
|
||||||
// Ignore for now
|
// Ignore for now
|
||||||
}
|
}
|
||||||
|
SBlockPickRequest::PACKET_ID => {
|
||||||
|
self.handle_block_pick_request(player, SBlockPickRequest::read(reader)?)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
SMobEquipment::PACKET_ID => {
|
||||||
|
self.handle_mob_equipment(player, SMobEquipment::read(reader)?)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Bedrock: Received Unknown Game packet: {}", packet.id);
|
warn!("Bedrock: Received Unknown Game packet: {}", packet.id);
|
||||||
}
|
}
|
||||||
@ -1111,8 +1125,24 @@ impl BedrockClient {
|
|||||||
payload: &mut Cursor<&[u8]>,
|
payload: &mut Cursor<&[u8]>,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
socket: &UdpSocket,
|
socket: &UdpSocket,
|
||||||
|
be_clients: &Arc<Mutex<HashMap<SocketAddr, Arc<Self>>>>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match i32::from(packet_id) {
|
let packet_id_i32 = i32::from(packet_id);
|
||||||
|
if packet_id_i32 == SOpenConnectionRequest1::PACKET_ID {
|
||||||
|
let old_client = {
|
||||||
|
let mut clients_guard = be_clients.lock().await;
|
||||||
|
clients_guard.remove(&addr)
|
||||||
|
};
|
||||||
|
if let Some(client) = old_client {
|
||||||
|
debug!(
|
||||||
|
"Closing old Bedrock client connection for {} due to new connection request",
|
||||||
|
addr
|
||||||
|
);
|
||||||
|
client.close().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match packet_id_i32 {
|
||||||
SUnconnectedPing::PACKET_ID => {
|
SUnconnectedPing::PACKET_ID => {
|
||||||
Self::handle_unconnected_ping(
|
Self::handle_unconnected_ping(
|
||||||
server,
|
server,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -90,7 +90,7 @@ use pumpkin_protocol::{
|
|||||||
bedrock::{
|
bedrock::{
|
||||||
client::{
|
client::{
|
||||||
add_player::CAddPlayer,
|
add_player::CAddPlayer,
|
||||||
creative_content::{CCreativeContent, Group},
|
creative_content::{CCreativeContent, CreativeCategory, Entry, Group},
|
||||||
gamerules_changed::GameRules,
|
gamerules_changed::GameRules,
|
||||||
player_list::{CPlayerList, PlayerListEntry, Skin},
|
player_list::{CPlayerList, PlayerListEntry, Skin},
|
||||||
remove_actor::CRemoveActor,
|
remove_actor::CRemoveActor,
|
||||||
@ -1755,6 +1755,8 @@ impl World {
|
|||||||
player: Arc<Player>,
|
player: Arc<Player>,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
) {
|
) {
|
||||||
|
static CREATIVE_CONTENT: std::sync::OnceLock<(Vec<Group>, Vec<Entry>)> =
|
||||||
|
std::sync::OnceLock::new();
|
||||||
let level_info = server.level_info.load();
|
let level_info = server.level_info.load();
|
||||||
let weather = self.weather.lock().await;
|
let weather = self.weather.lock().await;
|
||||||
let runtime_id = player.entity_id() as u64;
|
let runtime_id = player.entity_id() as u64;
|
||||||
@ -1909,16 +1911,65 @@ impl World {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
client
|
let (groups, entries) = CREATIVE_CONTENT.get_or_init(|| {
|
||||||
.send_game_packet(&CCreativeContent {
|
let groups = pumpkin_data::bedrock_creative::CREATIVE_GROUPS
|
||||||
groups: &[Group {
|
.iter()
|
||||||
creative_category:
|
.map(|g| {
|
||||||
pumpkin_protocol::bedrock::client::CreativeCategory::Construction,
|
let creative_category = match g.category {
|
||||||
name: String::new(),
|
1 => CreativeCategory::Construction,
|
||||||
icon_item: NetworkItemDescriptor::default(),
|
2 => CreativeCategory::Nature,
|
||||||
}],
|
3 => CreativeCategory::Equipment,
|
||||||
entries: &[],
|
4 => CreativeCategory::Items,
|
||||||
|
5 => CreativeCategory::CommandOnly,
|
||||||
|
_ => CreativeCategory::Undefined,
|
||||||
|
};
|
||||||
|
let icon_item = if g.icon_item_id != 0 {
|
||||||
|
NetworkItemDescriptor {
|
||||||
|
id: VarInt::from(g.icon_item_id),
|
||||||
|
stack_size: 1,
|
||||||
|
aux_value: VarUInt(g.icon_item_aux_value),
|
||||||
|
block_runtime_id: VarInt(0),
|
||||||
|
nbt_data: pumpkin_nbt::Nbt::default(),
|
||||||
|
place_on_blocks: Vec::new(),
|
||||||
|
destroy_blocks: Vec::new(),
|
||||||
|
shield_blocking_tick: 0,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NetworkItemDescriptor::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
Group {
|
||||||
|
creative_category,
|
||||||
|
name: g.name.to_string(),
|
||||||
|
icon_item,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let entries = pumpkin_data::bedrock_creative::CREATIVE_ENTRIES
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, e)| Entry {
|
||||||
|
id: VarUInt((i + 1) as u32),
|
||||||
|
item: NetworkItemDescriptor {
|
||||||
|
id: VarInt::from(e.item_id),
|
||||||
|
stack_size: 1,
|
||||||
|
aux_value: VarUInt(e.item_aux_value),
|
||||||
|
block_runtime_id: VarInt(0),
|
||||||
|
nbt_data: pumpkin_nbt::Nbt::default(),
|
||||||
|
place_on_blocks: Vec::new(),
|
||||||
|
destroy_blocks: Vec::new(),
|
||||||
|
shield_blocking_tick: 0,
|
||||||
|
},
|
||||||
|
group_index: VarUInt(e.group_index),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
(groups, entries)
|
||||||
|
});
|
||||||
|
|
||||||
|
client
|
||||||
|
.send_game_packet(&CCreativeContent { groups, entries })
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
client
|
client
|
||||||
@ -4982,7 +5033,7 @@ impl World {
|
|||||||
Self::broadcast_java_grouped(je_packet, je_recipients_by_version);
|
Self::broadcast_java_grouped(je_packet, je_recipients_by_version);
|
||||||
|
|
||||||
for recipient in bedrock_recipients {
|
for recipient in bedrock_recipients {
|
||||||
recipient.send_game_packet(be_packet).await;
|
recipient.enqueue_packet(be_packet).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user