feat(bedrock): add container open

This commit is contained in:
Alexander Medvedev
2026-06-07 21:12:16 +02:00
parent e47695d810
commit b08be9e158
4 changed files with 230 additions and 39 deletions

View File

@@ -1,4 +1,4 @@
use std::io::{Error, Write};
use std::io::{Error, Read, Write};
use std::num::NonZeroI32;
use pumpkin_data::item::{BedrockItem, JavaToBedrockItemMapping};
@@ -7,10 +7,10 @@ use pumpkin_nbt::Nbt;
use crate::{
codec::{var_int::VarInt, var_uint::VarUInt},
serial::PacketWrite,
serial::{PacketRead, PacketWrite},
};
#[derive(Default, Clone)]
#[derive(Default, Clone, Debug)]
pub struct NetworkItemDescriptor {
// I hate mojang
// https://mojang.github.io/bedrock-protocol-docs/html/NetworkItemInstanceDescriptor.html
@@ -33,6 +33,36 @@ impl PacketWrite for NetworkItemDescriptor {
}
}
impl PacketRead for NetworkItemDescriptor {
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
let id = VarInt::read(buf)?;
if id.0 == 0 {
return Ok(Self::default());
}
let stack_size = u16::read(buf)?;
let aux_value = VarUInt::read(buf)?;
let has_net_id = bool::read(buf)?;
if has_net_id {
let _net_id = VarInt::read(buf)?;
}
let block_runtime_id = VarInt::read(buf)?;
let user_data_len = VarUInt::read(buf)?.0;
let mut user_data = vec![0u8; user_data_len as usize];
buf.read_exact(&mut user_data)?;
Ok(Self {
id,
stack_size,
aux_value,
block_runtime_id,
..Default::default()
})
}
}
impl NetworkItemDescriptor {
#[allow(clippy::option_option)]
fn write_with_net_id<W: Write>(

View File

@@ -3,10 +3,12 @@ use std::io::{Error, ErrorKind, Read};
use pumpkin_macros::packet;
use pumpkin_util::math::position::BlockPos;
use crate::bedrock::network_item::NetworkItemDescriptor;
use crate::{
codec::{var_int::VarInt, var_uint::VarUInt, var_ulong::VarULong},
serial::PacketRead,
};
use pumpkin_util::math::vector3::Vector3;
pub const WINDOW_ID_INVENTORY: i32 = 0;
pub const WINDOW_ID_OFF_HAND: i32 = 119;
@@ -55,9 +57,8 @@ pub struct InventoryAction {
pub window_id: Option<i32>,
pub source_flags: Option<u32>,
pub inventory_slot: u32,
// TODO
pub old_item: (),
pub new_item: (),
pub old_item: NetworkItemDescriptor,
pub new_item: NetworkItemDescriptor,
}
impl PacketRead for InventoryAction {
@@ -79,16 +80,16 @@ impl PacketRead for InventoryAction {
let inventory_slot = VarULong::read(buf)?.0 as u32;
// let old_item = ItemStack::read(buf)?;
// let new_item = ItemStack::read(buf)?;
let old_item = NetworkItemDescriptor::read(buf)?;
let new_item = NetworkItemDescriptor::read(buf)?;
Ok(Self {
source_type,
window_id,
source_flags,
inventory_slot,
old_item: (),
new_item: (),
old_item,
new_item,
})
}
}
@@ -99,29 +100,79 @@ pub struct NormalTransactionData;
#[derive(Debug, PacketRead)]
pub struct MismatchTransactionData;
#[derive(Debug, PacketRead)]
#[derive(Debug)]
pub struct UseItemTransactionData {
pub action_type: VarULong,
pub trigger_type: VarULong,
pub action_type: VarUInt,
pub trigger_type: VarUInt,
pub block_position: BlockPos,
pub block_face: VarInt,
pub hot_bar_slot: VarInt,
// TODO
pub item_in_hand: NetworkItemDescriptor,
pub player_position: Vector3<f32>,
pub click_position: Vector3<f32>,
pub block_runtime_id: VarUInt,
pub client_prediction: VarUInt,
pub client_cooldown_state: u8,
}
#[derive(Debug, PacketRead)]
impl PacketRead for UseItemTransactionData {
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
Ok(Self {
action_type: VarUInt::read(buf)?,
trigger_type: VarUInt::read(buf)?,
block_position: BlockPos::read(buf)?,
block_face: VarInt::read(buf)?,
hot_bar_slot: VarInt::read(buf)?,
item_in_hand: NetworkItemDescriptor::read(buf)?,
player_position: Vector3::read(buf)?,
click_position: Vector3::read(buf)?,
block_runtime_id: VarUInt::read(buf)?,
client_prediction: VarUInt::read(buf)?,
client_cooldown_state: u8::read(buf)?,
})
}
}
#[derive(Debug)]
pub struct UseItemOnEntityTransactionData {
pub target_entity_runtime_id: VarULong,
pub action_type: VarULong,
pub action_type: VarUInt,
pub hot_bar_slot: VarInt,
// TODO
pub item_in_hand: NetworkItemDescriptor,
pub player_position: Vector3<f32>,
pub click_position: Vector3<f32>,
}
#[derive(Debug, PacketRead)]
impl PacketRead for UseItemOnEntityTransactionData {
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
Ok(Self {
target_entity_runtime_id: VarULong::read(buf)?,
action_type: VarUInt::read(buf)?,
hot_bar_slot: VarInt::read(buf)?,
item_in_hand: NetworkItemDescriptor::read(buf)?,
player_position: Vector3::read(buf)?,
click_position: Vector3::read(buf)?,
})
}
}
#[derive(Debug)]
pub struct ReleaseItemTransactionData {
pub action_type: VarULong,
pub action_type: VarUInt,
pub hot_bar_slot: VarInt,
// TODO
pub item_in_hand: NetworkItemDescriptor,
pub head_position: Vector3<f32>,
}
impl PacketRead for ReleaseItemTransactionData {
fn read<R: Read>(buf: &mut R) -> Result<Self, Error> {
Ok(Self {
action_type: VarUInt::read(buf)?,
hot_bar_slot: VarInt::read(buf)?,
item_in_hand: NetworkItemDescriptor::read(buf)?,
head_position: Vector3::read(buf)?,
})
}
}
#[derive(Debug)]

View File

@@ -40,7 +40,7 @@ use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::particle::Particle;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockState, Enchantment, tag, translation};
use pumpkin_data::{Block, BlockState, Enchantment, screen::WindowType, tag, translation};
use pumpkin_inventory::player::{
player_inventory::PlayerInventory, player_screen_handler::PlayerScreenHandler,
};
@@ -54,7 +54,9 @@ use pumpkin_nbt::compound::NbtCompound;
use pumpkin_nbt::tag::NbtTag;
use pumpkin_protocol::IdOr;
use pumpkin_protocol::SoundEvent;
use pumpkin_protocol::bedrock::client::container_open::CContainerOpen;
use pumpkin_protocol::codec::var_int::VarInt;
use pumpkin_protocol::codec::var_long::VarLong;
use pumpkin_protocol::java::client::play::{
Animation, CAcknowledgeBlockChange, CActionBar, CChangeDifficulty, CCloseContainer,
CCombatDeath, CCustomPayload, CDisguisedChatMessage, CEntityAnimation, CEntityPositionSync,
@@ -3326,16 +3328,44 @@ impl Player {
.await
{
let screen_handler_temp = screen_handler.lock().await;
let sync_id = screen_handler_temp.sync_id();
let window_type = screen_handler_temp
.window_type()
.expect("Can't open PlayerScreenHandler");
let display_name = screen_handler_factory.get_display_name();
let java_packet =
COpenScreen::new(sync_id.into(), (window_type as i32).into(), &display_name);
let bedrock_window_type = match window_type {
WindowType::Crafting => 1,
WindowType::Furnace => 2,
WindowType::Enchantment => 3,
WindowType::BrewingStand => 4,
WindowType::Anvil => 5,
WindowType::Hopper => 8,
WindowType::Beacon => 13,
WindowType::BlastFurnace => 27,
WindowType::Smoker => 28,
WindowType::Stonecutter => 29,
WindowType::CartographyTable => 30,
WindowType::Grindstone => 26,
WindowType::Loom => 24,
WindowType::Smithing => 34,
_ => 0,
};
let bedrock_packet = CContainerOpen {
container_id: sync_id,
container_type: bedrock_window_type,
position: block_pos.unwrap_or(BlockPos::ZERO),
target_entity_id: VarLong(-1),
};
self.client
.enqueue_packet(&COpenScreen::new(
screen_handler_temp.sync_id().into(),
(screen_handler_temp
.window_type()
.expect("Can't open PlayerScreenHandler") as i32)
.into(),
&screen_handler_factory.get_display_name(),
))
.enqueue_packet_editioned(&java_packet, &bedrock_packet)
.await;
drop(screen_handler_temp);
self.on_screen_handler_opened(screen_handler.clone()).await;
*self.current_screen_handler.lock().await = screen_handler;
@@ -3366,16 +3396,42 @@ impl Player {
}
let screen_handler_temp = screen_handler.lock().await;
let sync_id = screen_handler_temp.sync_id();
let window_type = screen_handler_temp
.window_type()
.expect("Can't open PlayerScreenHandler");
let java_packet = COpenScreen::new(sync_id.into(), (window_type as i32).into(), &title);
let bedrock_window_type = match window_type {
WindowType::Crafting => 1,
WindowType::Furnace => 2,
WindowType::Enchantment => 3,
WindowType::BrewingStand => 4,
WindowType::Anvil => 5,
WindowType::Hopper => 8,
WindowType::Beacon => 13,
WindowType::BlastFurnace => 27,
WindowType::Smoker => 28,
WindowType::Stonecutter => 29,
WindowType::CartographyTable => 30,
WindowType::Grindstone => 26,
WindowType::Loom => 24,
WindowType::Smithing => 34,
_ => 0,
};
let bedrock_packet = CContainerOpen {
container_id: sync_id,
container_type: bedrock_window_type,
position: BlockPos::ZERO,
target_entity_id: VarLong(-1),
};
self.client
.enqueue_packet(&COpenScreen::new(
screen_handler_temp.sync_id().into(),
(screen_handler_temp
.window_type()
.expect("Can't open PlayerScreenHandler") as i32)
.into(),
&title,
))
.enqueue_packet_editioned(&java_packet, &bedrock_packet)
.await;
drop(screen_handler_temp);
self.on_screen_handler_opened(screen_handler.clone()).await;
*self.current_screen_handler.lock().await = screen_handler;

View File

@@ -29,6 +29,7 @@ use pumpkin_util::{GameMode, math::position::BlockPos, text::TextComponent};
use pumpkin_world::world::BlockFlags;
use crate::{
block::{BlockHitResult, registry::BlockActionResult},
entity::{EntityBase, player::Player},
net::{DisconnectReason, bedrock::BedrockClient},
plugin::player::{
@@ -38,6 +39,7 @@ use crate::{
server::{Server, seasonal_events},
world::chunker::{self},
};
use pumpkin_data::BlockDirection;
use tracing::{debug, info};
impl BedrockClient {
@@ -386,8 +388,60 @@ impl BedrockClient {
TransactionData::Mismatch(_data) => {
// TODO
}
TransactionData::UseItem(_data) => {
// TODO
TransactionData::UseItem(data) => {
let face = match data.block_face.0 {
0 => BlockDirection::Down,
2 => BlockDirection::North,
3 => BlockDirection::South,
4 => BlockDirection::West,
5 => BlockDirection::East,
_ => BlockDirection::Up,
};
let world = player.world();
let block = world.get_block(&data.block_position);
let server = world.server.upgrade().expect("Server is gone");
if data.action_type.0 == 0 {
// Click block
let held_item = player.inventory.held_item();
let result = server
.block_registry
.use_with_item(
block,
player,
&data.block_position,
&BlockHitResult {
face: &face,
cursor_pos: &data.click_position,
},
&held_item,
&server,
&world,
)
.await;
if result.consumes_action() {
return;
}
if matches!(result, BlockActionResult::PassToDefaultBlockAction) {
server
.block_registry
.on_use(
block,
player,
&data.block_position,
&BlockHitResult {
face: &face,
cursor_pos: &data.click_position,
},
&server,
&world,
)
.await;
}
}
}
TransactionData::UseItemOnEntity(data) => {
let target_runtime_id = data.target_entity_runtime_id.0 as i32;