feat: add Anvil
This commit is contained in:
parent
81cffe19a2
commit
8060283970
@ -225,10 +225,18 @@ impl DataComponentImpl for UnbreakableImpl {
|
|||||||
}
|
}
|
||||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct CustomNameImpl {
|
pub struct CustomNameImpl {
|
||||||
// TODO make TextComponent const
|
// TODO make TextComponent
|
||||||
pub name: &'static str,
|
pub name: String,
|
||||||
}
|
}
|
||||||
impl DataComponentImpl for CustomNameImpl {
|
impl DataComponentImpl for CustomNameImpl {
|
||||||
|
fn write_data(&self) -> NbtTag {
|
||||||
|
NbtTag::String(self.name.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_hash(&self) -> i32 {
|
||||||
|
get_str_hash(self.name.as_str()) as i32
|
||||||
|
}
|
||||||
|
|
||||||
default_impl!(CustomName);
|
default_impl!(CustomName);
|
||||||
}
|
}
|
||||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
|
|||||||
@ -288,6 +288,20 @@ impl ItemStack {
|
|||||||
self.item_count == 0 || self.item.id == Item::AIR.id
|
self.item_count == 0 || self.item.id == Item::AIR.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_custom_name(&mut self, name: String) {
|
||||||
|
use crate::data_component_impl::CustomNameImpl;
|
||||||
|
let component = Some(CustomNameImpl { name }.to_dyn());
|
||||||
|
if let Some(pos) = self
|
||||||
|
.patch
|
||||||
|
.iter()
|
||||||
|
.position(|(id, _)| *id == DataComponent::CustomName)
|
||||||
|
{
|
||||||
|
self.patch[pos].1 = component;
|
||||||
|
} else {
|
||||||
|
self.patch.push((DataComponent::CustomName, component));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn split(&mut self, amount: u8) -> Self {
|
pub fn split(&mut self, amount: u8) -> Self {
|
||||||
let min = amount.min(self.item_count);
|
let min = amount.min(self.item_count);
|
||||||
|
|||||||
231
pumpkin-inventory/src/anvil/anvil_screen_handler.rs
Normal file
231
pumpkin-inventory/src/anvil/anvil_screen_handler.rs
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
use std::any::Any;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use pumpkin_data::{item_stack::ItemStack, screen::WindowType};
|
||||||
|
use pumpkin_world::inventory::Inventory;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
player::player_inventory::PlayerInventory,
|
||||||
|
screen_handler::{
|
||||||
|
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
|
||||||
|
ScreenHandlerFuture,
|
||||||
|
},
|
||||||
|
slot::NormalSlot,
|
||||||
|
window_property::{Anvil, WindowProperty},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct AnvilScreenHandler {
|
||||||
|
pub inventory: Arc<dyn Inventory>,
|
||||||
|
behaviour: ScreenHandlerBehaviour,
|
||||||
|
pub rename_text: String,
|
||||||
|
pub repair_cost: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnvilScreenHandler {
|
||||||
|
#[expect(clippy::needless_pass_by_value)]
|
||||||
|
pub fn new(
|
||||||
|
sync_id: u8,
|
||||||
|
player_inventory: &Arc<PlayerInventory>,
|
||||||
|
inventory: Arc<dyn Inventory>,
|
||||||
|
) -> Self {
|
||||||
|
let mut handler = Self {
|
||||||
|
inventory: inventory.clone(),
|
||||||
|
behaviour: ScreenHandlerBehaviour::new(sync_id, Some(WindowType::Anvil)),
|
||||||
|
rename_text: String::new(),
|
||||||
|
repair_cost: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Anvil specific slots: 2 input, 1 output
|
||||||
|
for i in 0..3 {
|
||||||
|
handler.add_slot(Arc::new(NormalSlot::new(inventory.clone(), i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let player_inventory: Arc<dyn Inventory> = player_inventory.clone();
|
||||||
|
handler.add_player_slots(&player_inventory);
|
||||||
|
|
||||||
|
handler
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_item_name(&mut self, name: String) {
|
||||||
|
self.rename_text = name;
|
||||||
|
self.update_result_slot().await;
|
||||||
|
self.send_content_updates().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_result_slot(&mut self) {
|
||||||
|
let input_a = {
|
||||||
|
let lock = self.inventory.get_stack(0).await;
|
||||||
|
lock.lock().await.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
if input_a.is_empty() {
|
||||||
|
self.inventory.set_stack(2, ItemStack::EMPTY.clone()).await;
|
||||||
|
self.set_repair_cost(0).await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result_item = input_a.clone();
|
||||||
|
let mut cost = 0;
|
||||||
|
|
||||||
|
// Basic renaming logic for now
|
||||||
|
if !self.rename_text.is_empty() {
|
||||||
|
result_item.set_custom_name(self.rename_text.clone());
|
||||||
|
cost += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If combining with another item... we'll skip complex anvil logic for now
|
||||||
|
// and just support renaming.
|
||||||
|
if cost > 0 {
|
||||||
|
self.inventory.set_stack(2, result_item).await;
|
||||||
|
self.set_repair_cost(cost).await;
|
||||||
|
} else {
|
||||||
|
self.inventory.set_stack(2, ItemStack::EMPTY.clone()).await;
|
||||||
|
self.set_repair_cost(0).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_repair_cost(&mut self, cost: i16) {
|
||||||
|
self.repair_cost = cost;
|
||||||
|
if let Some(sync_handler) = self.behaviour.sync_handler.as_ref() {
|
||||||
|
let (property_id, property_value) =
|
||||||
|
WindowProperty::new(Anvil::RepairCost, cost).into_tuple();
|
||||||
|
sync_handler
|
||||||
|
.update_property(&self.behaviour, property_id as i32, property_value as i32)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScreenHandler for AnvilScreenHandler {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_behaviour(&self) -> &ScreenHandlerBehaviour {
|
||||||
|
&self.behaviour
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour {
|
||||||
|
&mut self.behaviour
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.default_on_closed(player).await;
|
||||||
|
self.inventory.on_close().await;
|
||||||
|
// Drop inputs from anvil
|
||||||
|
for i in 0..2 {
|
||||||
|
let stack = self.inventory.remove_stack(i).await;
|
||||||
|
if !stack.is_empty() {
|
||||||
|
player.drop_item(stack, false).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.inventory.set_stack(2, ItemStack::EMPTY.clone()).await;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quick_move<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
_player: &'a dyn InventoryPlayer,
|
||||||
|
slot_index: i32,
|
||||||
|
) -> ItemStackFuture<'a> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut stack_left = ItemStack::EMPTY.clone();
|
||||||
|
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
||||||
|
|
||||||
|
if slot.has_stack().await {
|
||||||
|
let slot_stack_lock = slot.get_stack().await;
|
||||||
|
let slot_stack_guard = slot_stack_lock.lock().await;
|
||||||
|
stack_left = slot_stack_guard.clone();
|
||||||
|
drop(slot_stack_guard);
|
||||||
|
|
||||||
|
let mut slot_stack_mut = slot_stack_lock.lock().await;
|
||||||
|
|
||||||
|
if slot_index < 3 {
|
||||||
|
// From anvil to player
|
||||||
|
if !self
|
||||||
|
.insert_item(
|
||||||
|
&mut slot_stack_mut,
|
||||||
|
3,
|
||||||
|
self.get_behaviour().slots.len() as i32,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
return ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// From player to anvil input 0 and 1
|
||||||
|
if !self.insert_item(&mut slot_stack_mut, 0, 2, false).await {
|
||||||
|
return ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if slot_stack_mut.is_empty() {
|
||||||
|
drop(slot_stack_mut);
|
||||||
|
slot.set_stack(ItemStack::EMPTY.clone()).await;
|
||||||
|
} else {
|
||||||
|
drop(slot_stack_mut);
|
||||||
|
slot.mark_dirty().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_left
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_slot_click<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
slot_index: i32,
|
||||||
|
button: i32,
|
||||||
|
action_type: pumpkin_protocol::java::server::play::SlotActionType,
|
||||||
|
player: &'a dyn InventoryPlayer,
|
||||||
|
) -> ScreenHandlerFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if slot_index == 2 {
|
||||||
|
// Taking from output slot
|
||||||
|
let result_slot = self.get_behaviour().slots[2].clone();
|
||||||
|
if result_slot.has_stack().await {
|
||||||
|
let result_stack = result_slot.get_cloned_stack().await;
|
||||||
|
if !result_stack.is_empty() {
|
||||||
|
if player.experience_level() >= self.repair_cost as i32
|
||||||
|
|| player.is_creative()
|
||||||
|
{
|
||||||
|
// Consume experience
|
||||||
|
if !player.is_creative() {
|
||||||
|
player
|
||||||
|
.add_experience_levels(-(self.repair_cost as i32))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume inputs
|
||||||
|
let input_a = self.inventory.get_stack(0).await;
|
||||||
|
let mut input_a = input_a.lock().await;
|
||||||
|
input_a.decrement(1); // Usually consumes 1
|
||||||
|
if input_a.is_empty() {
|
||||||
|
*input_a = ItemStack::EMPTY.clone();
|
||||||
|
}
|
||||||
|
drop(input_a);
|
||||||
|
self.get_behaviour().slots[0].mark_dirty().await;
|
||||||
|
} else {
|
||||||
|
// Cancel click
|
||||||
|
self.send_content_updates().await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.internal_on_slot_click(slot_index, button, action_type, player)
|
||||||
|
.await;
|
||||||
|
if slot_index == 0 || slot_index == 1 || slot_index == 2 {
|
||||||
|
self.update_result_slot().await;
|
||||||
|
self.send_content_updates().await;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
3
pumpkin-inventory/src/anvil/mod.rs
Normal file
3
pumpkin-inventory/src/anvil/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod anvil_screen_handler;
|
||||||
|
|
||||||
|
pub use anvil_screen_handler::*;
|
||||||
@ -30,6 +30,7 @@
|
|||||||
//! [`PlayerInventory`]: PlayerInventory
|
//! [`PlayerInventory`]: PlayerInventory
|
||||||
//! [`SyncHandler`]: sync_handler::SyncHandler
|
//! [`SyncHandler`]: sync_handler::SyncHandler
|
||||||
|
|
||||||
|
pub mod anvil;
|
||||||
pub mod brewing;
|
pub mod brewing;
|
||||||
pub mod container_click;
|
pub mod container_click;
|
||||||
pub mod crafting;
|
pub mod crafting;
|
||||||
|
|||||||
@ -133,6 +133,15 @@ pub trait InventoryPlayer: Send + Sync {
|
|||||||
/// Checks if the player has infinite materials (creative mode).
|
/// Checks if the player has infinite materials (creative mode).
|
||||||
fn has_infinite_materials(&self) -> bool;
|
fn has_infinite_materials(&self) -> bool;
|
||||||
|
|
||||||
|
/// Checks if the player is in creative mode.
|
||||||
|
fn is_creative(&self) -> bool;
|
||||||
|
|
||||||
|
/// Gets the player's experience level.
|
||||||
|
fn experience_level(&self) -> i32;
|
||||||
|
|
||||||
|
/// Adds or removes experience levels.
|
||||||
|
fn add_experience_levels(&self, levels: i32) -> PlayerFuture<'_, ()>;
|
||||||
|
|
||||||
/// Sends a full container content packet.
|
/// Sends a full container content packet.
|
||||||
fn enqueue_inventory_packet<'a>(
|
fn enqueue_inventory_packet<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
|||||||
@ -108,6 +108,14 @@ pub enum Anvil {
|
|||||||
RepairCost,
|
RepairCost,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WindowPropertyTrait for Anvil {
|
||||||
|
fn to_id(self) -> i16 {
|
||||||
|
match self {
|
||||||
|
Self::RepairCost => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Brewing stand window properties.
|
/// Brewing stand window properties.
|
||||||
pub enum BrewingStand {
|
pub enum BrewingStand {
|
||||||
/// Brewing progress (0-400).
|
/// Brewing progress (0-400).
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::codec::var_int::VarInt;
|
|||||||
use pumpkin_data::Enchantment;
|
use pumpkin_data::Enchantment;
|
||||||
use pumpkin_data::data_component::DataComponent;
|
use pumpkin_data::data_component::DataComponent;
|
||||||
use pumpkin_data::data_component_impl::{
|
use pumpkin_data::data_component_impl::{
|
||||||
ConsumableImpl, ConsumeAnimation, ConsumeEffect, DamageImpl, DataComponentImpl,
|
ConsumableImpl, ConsumeAnimation, ConsumeEffect, CustomNameImpl, DamageImpl, DataComponentImpl,
|
||||||
EnchantmentsImpl, EquipmentSlot, EquippableImpl, FireworkExplosionImpl, FireworkExplosionShape,
|
EnchantmentsImpl, EquipmentSlot, EquippableImpl, FireworkExplosionImpl, FireworkExplosionShape,
|
||||||
FireworksImpl, IDSet, IDSetContent, IdOr, ItemModelImpl, MaxStackSizeImpl, PotionContentsImpl,
|
FireworksImpl, IDSet, IDSetContent, IdOr, ItemModelImpl, MaxStackSizeImpl, PotionContentsImpl,
|
||||||
SoundEvent, StatusEffectInstance, StoredEnchantmentsImpl, UnbreakableImpl, get,
|
SoundEvent, StatusEffectInstance, StoredEnchantmentsImpl, UnbreakableImpl, get,
|
||||||
@ -346,6 +346,19 @@ impl DataComponentCodec<Self> for ItemModelImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DataComponentCodec<Self> for CustomNameImpl {
|
||||||
|
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
|
||||||
|
seq.serialize_field::<String>("", &self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
|
||||||
|
let name = seq
|
||||||
|
.next_element::<String>()?
|
||||||
|
.ok_or(de::Error::custom("No CustomNameImpl name string!"))?;
|
||||||
|
Ok(Self { name })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DataComponentCodec<Self> for ConsumableImpl {
|
impl DataComponentCodec<Self> for ConsumableImpl {
|
||||||
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
|
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
|
||||||
seq.serialize_field::<f32>("", &self.consume_seconds)?;
|
seq.serialize_field::<f32>("", &self.consume_seconds)?;
|
||||||
@ -820,6 +833,7 @@ pub fn deserialize<'a, A: SeqAccess<'a>>(
|
|||||||
DataComponent::FireworkExplosion => Ok(FireworkExplosionImpl::deserialize(seq)?.to_dyn()),
|
DataComponent::FireworkExplosion => Ok(FireworkExplosionImpl::deserialize(seq)?.to_dyn()),
|
||||||
DataComponent::Fireworks => Ok(FireworksImpl::deserialize(seq)?.to_dyn()),
|
DataComponent::Fireworks => Ok(FireworksImpl::deserialize(seq)?.to_dyn()),
|
||||||
DataComponent::ItemModel => Ok(ItemModelImpl::deserialize(seq)?.to_dyn()),
|
DataComponent::ItemModel => Ok(ItemModelImpl::deserialize(seq)?.to_dyn()),
|
||||||
|
DataComponent::CustomName => Ok(CustomNameImpl::deserialize(seq)?.to_dyn()),
|
||||||
DataComponent::Consumable => Ok(ConsumableImpl::deserialize(seq)?.to_dyn()),
|
DataComponent::Consumable => Ok(ConsumableImpl::deserialize(seq)?.to_dyn()),
|
||||||
DataComponent::Equippable => Ok(EquippableImpl::deserialize(seq)?.to_dyn()),
|
DataComponent::Equippable => Ok(EquippableImpl::deserialize(seq)?.to_dyn()),
|
||||||
DataComponent::StoredEnchantments => Ok(StoredEnchantmentsImpl::deserialize(seq)?.to_dyn()),
|
DataComponent::StoredEnchantments => Ok(StoredEnchantmentsImpl::deserialize(seq)?.to_dyn()),
|
||||||
@ -840,6 +854,7 @@ pub fn serialize<T: SerializeStruct>(
|
|||||||
DataComponent::FireworkExplosion => get::<FireworkExplosionImpl>(value).serialize(seq),
|
DataComponent::FireworkExplosion => get::<FireworkExplosionImpl>(value).serialize(seq),
|
||||||
DataComponent::Fireworks => get::<FireworksImpl>(value).serialize(seq),
|
DataComponent::Fireworks => get::<FireworksImpl>(value).serialize(seq),
|
||||||
DataComponent::ItemModel => get::<ItemModelImpl>(value).serialize(seq),
|
DataComponent::ItemModel => get::<ItemModelImpl>(value).serialize(seq),
|
||||||
|
DataComponent::CustomName => get::<CustomNameImpl>(value).serialize(seq),
|
||||||
DataComponent::Consumable => get::<ConsumableImpl>(value).serialize(seq),
|
DataComponent::Consumable => get::<ConsumableImpl>(value).serialize(seq),
|
||||||
DataComponent::Equippable => get::<EquippableImpl>(value).serialize(seq),
|
DataComponent::Equippable => get::<EquippableImpl>(value).serialize(seq),
|
||||||
DataComponent::StoredEnchantments => get::<StoredEnchantmentsImpl>(value).serialize(seq),
|
DataComponent::StoredEnchantments => get::<StoredEnchantmentsImpl>(value).serialize(seq),
|
||||||
|
|||||||
@ -31,6 +31,7 @@ mod player_rotation;
|
|||||||
mod player_session;
|
mod player_session;
|
||||||
mod recipe_book_change_settings;
|
mod recipe_book_change_settings;
|
||||||
mod recipe_book_seen_recipe;
|
mod recipe_book_seen_recipe;
|
||||||
|
mod rename_item;
|
||||||
mod select_trade;
|
mod select_trade;
|
||||||
mod set_command_block;
|
mod set_command_block;
|
||||||
mod set_creative_slot;
|
mod set_creative_slot;
|
||||||
@ -73,6 +74,7 @@ pub use player_rotation::*;
|
|||||||
pub use player_session::*;
|
pub use player_session::*;
|
||||||
pub use recipe_book_change_settings::*;
|
pub use recipe_book_change_settings::*;
|
||||||
pub use recipe_book_seen_recipe::*;
|
pub use recipe_book_seen_recipe::*;
|
||||||
|
pub use rename_item::*;
|
||||||
pub use select_trade::*;
|
pub use select_trade::*;
|
||||||
pub use set_command_block::*;
|
pub use set_command_block::*;
|
||||||
pub use set_creative_slot::*;
|
pub use set_creative_slot::*;
|
||||||
|
|||||||
9
pumpkin-protocol/src/java/server/play/rename_item.rs
Normal file
9
pumpkin-protocol/src/java/server/play/rename_item.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use pumpkin_data::packet::serverbound::PLAY_RENAME_ITEM;
|
||||||
|
use pumpkin_macros::java_packet;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[java_packet(PLAY_RENAME_ITEM)]
|
||||||
|
pub struct SRenameItem {
|
||||||
|
pub item_name: String,
|
||||||
|
}
|
||||||
@ -1,16 +1,44 @@
|
|||||||
use crate::block::blocks::falling::FallingBlock;
|
use crate::block::blocks::falling::FallingBlock;
|
||||||
|
use crate::block::registry::BlockActionResult;
|
||||||
use crate::block::{
|
use crate::block::{
|
||||||
BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
|
BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs,
|
||||||
PlacedArgs,
|
OnScheduledTickArgs, PlacedArgs,
|
||||||
};
|
};
|
||||||
|
|
||||||
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
|
||||||
|
use pumpkin_data::translation;
|
||||||
|
use pumpkin_inventory::anvil::AnvilScreenHandler;
|
||||||
|
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||||
|
use pumpkin_inventory::screen_handler::{
|
||||||
|
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
|
||||||
|
};
|
||||||
use pumpkin_macros::pumpkin_block_from_tag;
|
use pumpkin_macros::pumpkin_block_from_tag;
|
||||||
|
use pumpkin_util::text::TextComponent;
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
|
use pumpkin_world::inventory::SimpleInventory;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
#[pumpkin_block_from_tag("minecraft:anvil")]
|
#[pumpkin_block_from_tag("minecraft:anvil")]
|
||||||
pub struct AnvilBlock;
|
pub struct AnvilBlock;
|
||||||
|
|
||||||
impl BlockBehaviour for AnvilBlock {
|
impl BlockBehaviour for AnvilBlock {
|
||||||
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||||
|
Box::pin(async move {
|
||||||
|
args.player
|
||||||
|
.open_handled_screen(&AnvilScreenFactory, Some(*args.position))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
BlockActionResult::Success
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
FallingBlock::placed(&FallingBlock, args).await;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let dir = args
|
let dir = args
|
||||||
@ -27,9 +55,9 @@ impl BlockBehaviour for AnvilBlock {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
FallingBlock::placed(&FallingBlock, args).await;
|
FallingBlock::on_scheduled_tick(&FallingBlock, args).await;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,9 +69,27 @@ impl BlockBehaviour for AnvilBlock {
|
|||||||
async move { FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await },
|
async move { FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
|
}
|
||||||
|
|
||||||
|
struct AnvilScreenFactory;
|
||||||
|
|
||||||
|
impl ScreenHandlerFactory for AnvilScreenFactory {
|
||||||
|
fn create_screen_handler<'a>(
|
||||||
|
&'a self,
|
||||||
|
sync_id: u8,
|
||||||
|
player_inventory: &'a Arc<PlayerInventory>,
|
||||||
|
_player: &'a dyn InventoryPlayer,
|
||||||
|
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
FallingBlock::on_scheduled_tick(&FallingBlock, args).await;
|
let inventory = Arc::new(SimpleInventory::new(3));
|
||||||
|
let handler = AnvilScreenHandler::new(sync_id, player_inventory, inventory);
|
||||||
|
let concrete_arc = Arc::new(Mutex::new(handler));
|
||||||
|
|
||||||
|
Some(concrete_arc as SharedScreenHandler)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_display_name(&self) -> TextComponent {
|
||||||
|
TextComponent::translate(translation::CONTAINER_REPAIR, &[])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,7 +64,7 @@ use pumpkin_protocol::java::client::play::{
|
|||||||
CTitleAnimation, CTitleText, CUnloadChunk, CUpdateMobEffect, CUpdateTime, GameEvent, Metadata,
|
CTitleAnimation, CTitleText, CUnloadChunk, CUpdateMobEffect, CUpdateTime, GameEvent, Metadata,
|
||||||
PlayerAction, PlayerInfoFlags, PreviousMessage,
|
PlayerAction, PlayerInfoFlags, PreviousMessage,
|
||||||
};
|
};
|
||||||
use pumpkin_protocol::java::server::play::{SClickSlot, SlotActionType};
|
use pumpkin_protocol::java::server::play::{SClickSlot, SRenameItem, SlotActionType};
|
||||||
use pumpkin_util::math::{
|
use pumpkin_util::math::{
|
||||||
boundingbox::BoundingBox, experience, position::BlockPos, vector2::Vector2, vector3::Vector3,
|
boundingbox::BoundingBox, experience, position::BlockPos, vector2::Vector2, vector3::Vector3,
|
||||||
};
|
};
|
||||||
@ -2933,6 +2933,19 @@ impl Player {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn on_rename_item(self: &Arc<Self>, packet: SRenameItem) {
|
||||||
|
self.update_last_action_time();
|
||||||
|
let screen_handler_arc = self.current_screen_handler.lock().await.clone();
|
||||||
|
let mut screen_handler = screen_handler_arc.lock().await;
|
||||||
|
|
||||||
|
if let Some(anvil_handler) = screen_handler
|
||||||
|
.as_any_mut()
|
||||||
|
.downcast_mut::<pumpkin_inventory::anvil::AnvilScreenHandler>()
|
||||||
|
{
|
||||||
|
anvil_handler.update_item_name(packet.item_name).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn open_handled_screen(
|
pub async fn open_handled_screen(
|
||||||
&self,
|
&self,
|
||||||
screen_handler_factory: &dyn ScreenHandlerFactory,
|
screen_handler_factory: &dyn ScreenHandlerFactory,
|
||||||
@ -4060,6 +4073,21 @@ impl InventoryPlayer for Player {
|
|||||||
self.gamemode.load() == GameMode::Creative
|
self.gamemode.load() == GameMode::Creative
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_creative(&self) -> bool {
|
||||||
|
self.gamemode.load() == GameMode::Creative
|
||||||
|
}
|
||||||
|
|
||||||
|
fn experience_level(&self) -> i32 {
|
||||||
|
self.experience_level
|
||||||
|
.load(std::sync::atomic::Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_experience_levels(&self, levels: i32) -> PlayerFuture<'_, ()> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.add_experience_levels(levels).await;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_inventory(&self) -> Arc<PlayerInventory> {
|
fn get_inventory(&self) -> Arc<PlayerInventory> {
|
||||||
self.inventory.clone()
|
self.inventory.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,9 @@ impl ItemBehaviour for NameTagItem {
|
|||||||
&& let Some(name) = item.get_data_component::<CustomNameImpl>()
|
&& let Some(name) = item.get_data_component::<CustomNameImpl>()
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
entity.set_custom_name(TextComponent::text(name.name)).await;
|
entity
|
||||||
|
.set_custom_name(TextComponent::text(name.name.clone()))
|
||||||
|
.await;
|
||||||
item.decrement_unless_creative(player.gamemode.load(), 1);
|
item.decrement_unless_creative(player.gamemode.load(), 1);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -12,8 +12,8 @@ use pumpkin_protocol::java::server::play::{
|
|||||||
SPaddleBoat, SPickItemFromBlock, SPlaceRecipe, SPlayPingRequest, SPlayerAbilities,
|
SPaddleBoat, SPickItemFromBlock, SPlaceRecipe, SPlayPingRequest, SPlayerAbilities,
|
||||||
SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerLoaded, SPlayerPosition,
|
SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerLoaded, SPlayerPosition,
|
||||||
SPlayerPositionRotation, SPlayerRotation, SPlayerSession, SRecipeBookChangeSettings,
|
SPlayerPositionRotation, SPlayerRotation, SPlayerSession, SRecipeBookChangeSettings,
|
||||||
SRecipeBookSeenRecipe, SSelectTrade, SSetCommandBlock, SSetCreativeSlot, SSetHeldItem,
|
SRecipeBookSeenRecipe, SRenameItem, SSelectTrade, SSetCommandBlock, SSetCreativeSlot,
|
||||||
SSetPlayerGround, SSwingArm, SUpdateSign, SUseItem, SUseItemOn,
|
SSetHeldItem, SSetPlayerGround, SSwingArm, SUpdateSign, SUseItem, SUseItemOn,
|
||||||
};
|
};
|
||||||
use pumpkin_protocol::packet::MultiVersionJavaPacket;
|
use pumpkin_protocol::packet::MultiVersionJavaPacket;
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
@ -887,6 +887,11 @@ impl JavaClient {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
id if id == SRenameItem::to_id(version) => {
|
||||||
|
player
|
||||||
|
.on_rename_item(SRenameItem::read(payload, &version)?)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
id if id == SPlaceRecipe::to_id(version) => {
|
id if id == SPlaceRecipe::to_id(version) => {
|
||||||
self.handle_place_recipe(player, SPlaceRecipe::read(payload, &version)?)
|
self.handle_place_recipe(player, SPlaceRecipe::read(payload, &version)?)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user