mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
feat(bedrock): add creative content
This commit is contained in:
33
Cargo.lock
generated
33
Cargo.lock
generated
@@ -270,9 +270,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.1"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||
checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
|
||||
|
||||
[[package]]
|
||||
name = "cap-fs-ext"
|
||||
@@ -471,9 +471,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cmov"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
|
||||
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
@@ -919,9 +919,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42a0d26b245348befa0c121944541476763dcc46ede886c88f9d12e1697d27c3"
|
||||
checksum = "97bb4a855e3b10f84c4e7e895a7de01db7f9a7b7eb7f73ed9773fd52ac686451"
|
||||
dependencies = [
|
||||
"cpubits",
|
||||
"ctutils",
|
||||
@@ -956,7 +956,7 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21f41f23de7d24cdbda7f0c4d9c0351f99a4ceb258ef30e5c1927af8987ffe5a"
|
||||
dependencies = [
|
||||
"crypto-bigint 0.7.3",
|
||||
"crypto-bigint 0.7.4",
|
||||
"libm",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
@@ -1890,6 +1890,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
@@ -2841,7 +2850,7 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"colored",
|
||||
"crypto-bigint 0.7.3",
|
||||
"crypto-bigint 0.7.4",
|
||||
"ecdsa",
|
||||
"enum_dispatch",
|
||||
"md5",
|
||||
@@ -2877,7 +2886,7 @@ dependencies = [
|
||||
"enum_dispatch",
|
||||
"flate2",
|
||||
"futures",
|
||||
"itertools 0.14.0",
|
||||
"itertools 0.15.0",
|
||||
"lz4-java-wrc",
|
||||
"pumpkin-config",
|
||||
"pumpkin-data",
|
||||
@@ -3067,7 +3076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3caf"
|
||||
dependencies = [
|
||||
"const-oid 0.10.2",
|
||||
"crypto-bigint 0.7.3",
|
||||
"crypto-bigint 0.7.4",
|
||||
"crypto-primes",
|
||||
"digest 0.11.3",
|
||||
"pkcs1",
|
||||
@@ -3950,9 +3959,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.20.0"
|
||||
version = "1.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
|
||||
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
|
||||
@@ -102,7 +102,7 @@ syn = { version = "2.0", default-features = false, features = ["printing"] }
|
||||
|
||||
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
|
||||
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 }
|
||||
crossbeam-utils = { version = "0.8.21", default-features = false, features = ["std"] }
|
||||
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 }
|
||||
ecdsa = { version = "0.16.9", default-features = false, features = ["std"] }
|
||||
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 }
|
||||
hmac = { version = "=0.13.0", default-features = false }
|
||||
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"] }
|
||||
lru = { version = "0.18.0", default-features = false }
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
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! {
|
||||
#[allow(clippy::wildcard_imports, clippy::enum_glob_use, clippy::too_many_lines)]
|
||||
use crate::data_component::DataComponent::*;
|
||||
@@ -1371,6 +1405,17 @@ pub fn build() -> TokenStream {
|
||||
_ => 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 attributes;
|
||||
mod bedrock_creative;
|
||||
mod biome;
|
||||
mod bitsets;
|
||||
mod block;
|
||||
@@ -78,6 +79,7 @@ pub fn main() {
|
||||
|
||||
let mut build_functions: Vec<(BuilderFn, &str)> = vec![
|
||||
(advancement::build, "advancement.rs"),
|
||||
(bedrock_creative::build, "bedrock_creative.rs"),
|
||||
(packet::build, "packet.rs"),
|
||||
(screen::build, "screen.rs"),
|
||||
(particle::build, "particle.rs"),
|
||||
|
||||
@@ -47,10 +47,12 @@ default = [
|
||||
"villager",
|
||||
"placed_feature",
|
||||
"configured_feature",
|
||||
"slot_ranges"
|
||||
"slot_ranges",
|
||||
"bedrock_creative"
|
||||
]
|
||||
|
||||
advancement = []
|
||||
bedrock_creative = []
|
||||
item = []
|
||||
packet = []
|
||||
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"]
|
||||
pub mod sound_id_remap;
|
||||
|
||||
#[cfg(feature = "bedrock_creative")]
|
||||
#[rustfmt::skip]
|
||||
#[path = "generated/bedrock_creative.rs"]
|
||||
pub mod bedrock_creative;
|
||||
|
||||
#[cfg(feature = "tag")]
|
||||
#[rustfmt::skip]
|
||||
#[path = "generated/tag.rs"]
|
||||
|
||||
@@ -13,7 +13,7 @@ query = []
|
||||
|
||||
[dependencies]
|
||||
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-world.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 inventory_content;
|
||||
pub mod item_registry;
|
||||
pub mod item_stack_response;
|
||||
pub mod level_chunk;
|
||||
pub mod level_event;
|
||||
pub mod modal_form_request;
|
||||
@@ -56,6 +57,8 @@ pub use disconnect_player::*;
|
||||
pub use gamerules_changed::*;
|
||||
pub use handshake::*;
|
||||
pub use inventory_content::*;
|
||||
pub use item_registry::*;
|
||||
pub use item_stack_response::*;
|
||||
pub use level_chunk::*;
|
||||
pub use level_event::*;
|
||||
pub use modal_form_request::*;
|
||||
|
||||
@@ -115,7 +115,7 @@ impl From<&ItemStack> for NetworkItemDescriptor {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct NetworkItemStackDescriptor {
|
||||
// I hate mojang
|
||||
// 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 {
|
||||
fn from(stack: &ItemStack) -> Self {
|
||||
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 container_name: ContainerName,
|
||||
pub dynamic_id: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum ContainerName {
|
||||
AnvilInput,
|
||||
@@ -233,9 +240,135 @@ pub enum ContainerName {
|
||||
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 {
|
||||
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||
(*self as u8).write(writer)?;
|
||||
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 animate;
|
||||
pub mod block_pick_request;
|
||||
pub mod client_cache_status;
|
||||
pub mod command_request;
|
||||
pub mod container_close;
|
||||
@@ -7,8 +8,10 @@ pub mod emote;
|
||||
pub mod emote_list;
|
||||
pub mod interaction;
|
||||
pub mod inventory_transaction;
|
||||
pub mod item_stack_request;
|
||||
pub mod loading_screen;
|
||||
pub mod login;
|
||||
pub mod mob_equipment;
|
||||
pub mod modal_form_response;
|
||||
pub mod player_action;
|
||||
pub mod player_auth_input;
|
||||
@@ -21,6 +24,7 @@ pub mod text;
|
||||
|
||||
pub use actor_event::*;
|
||||
pub use animate::*;
|
||||
pub use block_pick_request::*;
|
||||
pub use client_cache_status::*;
|
||||
pub use command_request::*;
|
||||
pub use container_close::*;
|
||||
@@ -28,8 +32,10 @@ pub use emote::*;
|
||||
pub use emote_list::*;
|
||||
pub use interaction::{Action as InteractAction, SInteraction};
|
||||
pub use inventory_transaction::*;
|
||||
pub use item_stack_request::*;
|
||||
pub use loading_screen::*;
|
||||
pub use login::*;
|
||||
pub use mob_equipment::*;
|
||||
pub use modal_form_response::*;
|
||||
pub use player_action::{Action as PlayerActionType, SPlayerAction};
|
||||
pub use player_auth_input::*;
|
||||
|
||||
@@ -2488,7 +2488,7 @@ impl Entity {
|
||||
MetadataValue::Long(self.bedrock_flags_two.load(Ordering::Relaxed)),
|
||||
);
|
||||
client
|
||||
.send_game_packet(&CSetActorData {
|
||||
.enqueue_packet(&CSetActorData {
|
||||
actor_runtime_id: VarULong(self.entity_id as u64),
|
||||
metadata,
|
||||
synced_properties: PropertySyncData {
|
||||
|
||||
@@ -3400,16 +3400,40 @@ impl Player {
|
||||
}
|
||||
|
||||
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
|
||||
.enqueue_packet(&CCloseContainer::new(
|
||||
self.current_screen_handler
|
||||
.lock()
|
||||
.await
|
||||
.lock()
|
||||
.await
|
||||
.sync_id()
|
||||
.into(),
|
||||
))
|
||||
.enqueue_packet_editioned(
|
||||
&CCloseContainer::new(sync_id.into()),
|
||||
&pumpkin_protocol::bedrock::server::container_close::SContainerClose {
|
||||
container_id: sync_id,
|
||||
container_type: bedrock_window_type,
|
||||
server_initiated: true,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
self.on_handled_screen_closed().await;
|
||||
}
|
||||
|
||||
@@ -504,6 +504,13 @@ impl PumpkinServer {
|
||||
let be_clients = bedrock_clients.clone();
|
||||
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 client = clients_guard.entry(client_addr).or_insert_with(|| {
|
||||
is_new = true;
|
||||
@@ -525,9 +532,10 @@ impl PumpkinServer {
|
||||
tasks.spawn(async move {
|
||||
let login_result = client_clone.handle_login_sequence(&server_clone).await;
|
||||
|
||||
match login_result {
|
||||
match login_result {
|
||||
PacketHandlerResult::Stop => {
|
||||
client_clone.close().await;
|
||||
client_clone.await_tasks().await;
|
||||
}
|
||||
PacketHandlerResult::ReadyToPlay(profile, config) => {
|
||||
if let Some((player, _world)) = server_clone
|
||||
@@ -539,6 +547,7 @@ impl PumpkinServer {
|
||||
client_clone.progress_player_packets(&player, &server_clone).await;
|
||||
|
||||
client_clone.close().await;
|
||||
client_clone.await_tasks().await;
|
||||
player.remove().await;
|
||||
server_clone.remove_player(&player).await;
|
||||
if let Err(e) = server_clone.player_data_storage
|
||||
@@ -564,7 +573,8 @@ impl PumpkinServer {
|
||||
id,
|
||||
&mut Cursor::new(&udp_buf[1..len]),
|
||||
client_addr,
|
||||
sock
|
||||
sock,
|
||||
bedrock_clients,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ use pumpkin_protocol::{
|
||||
packet_encoder::UDPNetworkEncoder,
|
||||
server::{
|
||||
animate::SAnimate,
|
||||
block_pick_request::SBlockPickRequest,
|
||||
client_cache_status::SClientCacheStatus,
|
||||
command_request::SCommandRequest,
|
||||
container_close::SContainerClose,
|
||||
@@ -38,6 +39,7 @@ use pumpkin_protocol::{
|
||||
inventory_transaction::SInventoryTransaction,
|
||||
loading_screen::SLoadingScreen,
|
||||
login::SLogin,
|
||||
mob_equipment::SMobEquipment,
|
||||
player_action::SPlayerAction,
|
||||
player_auth_input::SPlayerAuthInput,
|
||||
raknet::{
|
||||
@@ -136,6 +138,7 @@ pub struct BedrockClient {
|
||||
output_ordered_index: AtomicU32,
|
||||
/// The next form ID to use for custom forms.
|
||||
pub next_form_id: AtomicU32,
|
||||
pub inventory_opened: AtomicBool,
|
||||
/// An notifier that is triggered when this client is closed.
|
||||
close_token: CancellationToken,
|
||||
last_seen: Arc<AtomicCell<std::time::Instant>>,
|
||||
@@ -184,6 +187,7 @@ impl BedrockClient {
|
||||
output_sequenced_index: AtomicU32::new(0),
|
||||
output_ordered_index: AtomicU32::new(0),
|
||||
next_form_id: AtomicU32::new(0),
|
||||
inventory_opened: AtomicBool::new(false),
|
||||
compounds: Arc::new(Mutex::new(HashMap::new())),
|
||||
close_token: CancellationToken::new(),
|
||||
last_seen: Arc::new(AtomicCell::new(std::time::Instant::now())),
|
||||
@@ -650,12 +654,11 @@ impl BedrockClient {
|
||||
}
|
||||
self.close_token.cancel();
|
||||
self.be_clients.lock().await.remove(&self.address);
|
||||
}
|
||||
|
||||
pub async fn await_tasks(&self) {
|
||||
self.tasks.close();
|
||||
self.tasks.wait().await;
|
||||
|
||||
if let Some(player) = self.player.lock().await.as_ref() {
|
||||
player.remove().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_closed(&self) -> bool {
|
||||
@@ -1011,6 +1014,9 @@ impl BedrockClient {
|
||||
SInventoryTransaction::PACKET_ID => {
|
||||
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 => {
|
||||
self.handle_interaction(player, SInteraction::read(reader)?)
|
||||
.await;
|
||||
@@ -1059,6 +1065,14 @@ impl BedrockClient {
|
||||
SLoadingScreen::PACKET_ID => {
|
||||
// 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);
|
||||
}
|
||||
@@ -1111,8 +1125,24 @@ impl BedrockClient {
|
||||
payload: &mut Cursor<&[u8]>,
|
||||
addr: SocketAddr,
|
||||
socket: &UdpSocket,
|
||||
be_clients: &Arc<Mutex<HashMap<SocketAddr, Arc<Self>>>>,
|
||||
) -> 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 => {
|
||||
Self::handle_unconnected_ping(
|
||||
server,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -90,7 +90,7 @@ use pumpkin_protocol::{
|
||||
bedrock::{
|
||||
client::{
|
||||
add_player::CAddPlayer,
|
||||
creative_content::{CCreativeContent, Group},
|
||||
creative_content::{CCreativeContent, CreativeCategory, Entry, Group},
|
||||
gamerules_changed::GameRules,
|
||||
player_list::{CPlayerList, PlayerListEntry, Skin},
|
||||
remove_actor::CRemoveActor,
|
||||
@@ -1755,6 +1755,8 @@ impl World {
|
||||
player: Arc<Player>,
|
||||
server: &Server,
|
||||
) {
|
||||
static CREATIVE_CONTENT: std::sync::OnceLock<(Vec<Group>, Vec<Entry>)> =
|
||||
std::sync::OnceLock::new();
|
||||
let level_info = server.level_info.load();
|
||||
let weather = self.weather.lock().await;
|
||||
let runtime_id = player.entity_id() as u64;
|
||||
@@ -1909,16 +1911,65 @@ impl World {
|
||||
})
|
||||
.await;
|
||||
|
||||
let (groups, entries) = CREATIVE_CONTENT.get_or_init(|| {
|
||||
let groups = pumpkin_data::bedrock_creative::CREATIVE_GROUPS
|
||||
.iter()
|
||||
.map(|g| {
|
||||
let creative_category = match g.category {
|
||||
1 => CreativeCategory::Construction,
|
||||
2 => CreativeCategory::Nature,
|
||||
3 => CreativeCategory::Equipment,
|
||||
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: &[Group {
|
||||
creative_category:
|
||||
pumpkin_protocol::bedrock::client::CreativeCategory::Construction,
|
||||
name: String::new(),
|
||||
icon_item: NetworkItemDescriptor::default(),
|
||||
}],
|
||||
entries: &[],
|
||||
})
|
||||
.send_game_packet(&CCreativeContent { groups, entries })
|
||||
.await;
|
||||
|
||||
client
|
||||
@@ -4982,7 +5033,7 @@ impl World {
|
||||
Self::broadcast_java_grouped(je_packet, je_recipients_by_version);
|
||||
|
||||
for recipient in bedrock_recipients {
|
||||
recipient.send_game_packet(be_packet).await;
|
||||
recipient.enqueue_packet(be_packet).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user