diff --git a/pumpkin-data/build/item.rs b/pumpkin-data/build/item.rs index 34175f265..4c8227210 100644 --- a/pumpkin-data/build/item.rs +++ b/pumpkin-data/build/item.rs @@ -39,6 +39,8 @@ pub struct ItemComponents { pub consumable: Option, #[serde(rename = "minecraft:blocks_attacks")] pub blocks_attacks: Option, + #[serde(rename = "minecraft:death_protection")] + pub death_protection: Option, } impl ToTokens for ItemComponents { @@ -237,6 +239,10 @@ impl ToTokens for ItemComponents { tokens.extend(quote! { (BlocksAttacks, &BlocksAttacksImpl), }); }; + if self.death_protection.is_some() { + tokens.extend(quote! { (DeathProtection, &DeathProtectionImpl), }); + }; + if let Some(equippable) = &self.equippable { let slot = match equippable.slot.as_str() { "mainhand" => quote! { &EquipmentSlot::MAIN_HAND }, @@ -397,6 +403,11 @@ pub struct Consumable { consume_seconds: Option, // TODO } +#[derive(Deserialize, Clone, Debug)] +pub struct DeathProtection { + // TODO +} + #[derive(Deserialize, Clone, Debug)] pub struct BlocksAttacks { // TODO diff --git a/pumpkin-data/src/data_component_impl.rs b/pumpkin-data/src/data_component_impl.rs index 3b705d608..20ec73065 100644 --- a/pumpkin-data/src/data_component_impl.rs +++ b/pumpkin-data/src/data_component_impl.rs @@ -571,6 +571,9 @@ pub struct GliderImpl; pub struct TooltipStyleImpl; #[derive(Clone, Debug, Hash, PartialEq)] pub struct DeathProtectionImpl; +impl DataComponentImpl for DeathProtectionImpl { + default_impl!(DeathProtection); +} #[derive(Clone, Debug, Hash, PartialEq)] pub struct BlocksAttacksImpl; diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index ce3dd8d54..6c729b938 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -12,4 +12,33 @@ pub mod slot; pub mod sync_handler; pub mod window_property; +use std::collections::HashMap; + pub use error::InventoryError; +use pumpkin_data::data_component_impl::EquipmentSlot; + +use crate::player::player_inventory::PlayerInventory; + +pub fn build_equipment_slots() -> HashMap { + let mut equipment_slots = HashMap::new(); + equipment_slots.insert( + EquipmentSlot::FEET.get_offset_entity_slot_id(PlayerInventory::MAIN_SIZE as i32) as usize, + EquipmentSlot::FEET, + ); + equipment_slots.insert( + EquipmentSlot::LEGS.get_offset_entity_slot_id(PlayerInventory::MAIN_SIZE as i32) as usize, + EquipmentSlot::LEGS, + ); + equipment_slots.insert( + EquipmentSlot::CHEST.get_offset_entity_slot_id(PlayerInventory::MAIN_SIZE as i32) as usize, + EquipmentSlot::CHEST, + ); + equipment_slots.insert( + EquipmentSlot::HEAD.get_offset_entity_slot_id(PlayerInventory::MAIN_SIZE as i32) as usize, + EquipmentSlot::HEAD, + ); + + equipment_slots.insert(PlayerInventory::OFF_HAND_SLOT, EquipmentSlot::OFF_HAND); + equipment_slots.insert(PlayerInventory::OFF_HAND_SLOT, EquipmentSlot::OFF_HAND); + equipment_slots +} diff --git a/pumpkin-inventory/src/player/player_inventory.rs b/pumpkin-inventory/src/player/player_inventory.rs index bce77282c..086e3db5b 100644 --- a/pumpkin-inventory/src/player/player_inventory.rs +++ b/pumpkin-inventory/src/player/player_inventory.rs @@ -3,6 +3,7 @@ use crate::screen_handler::InventoryPlayer; use async_trait::async_trait; use pumpkin_data::data_component_impl::EquipmentSlot; use pumpkin_protocol::java::client::play::CSetPlayerInventory; +use pumpkin_util::Hand; use pumpkin_world::inventory::split_stack; use pumpkin_world::inventory::{Clearable, Inventory}; use pumpkin_world::item::ItemStack; @@ -16,22 +17,25 @@ use tokio::sync::Mutex; #[derive(Debug)] pub struct PlayerInventory { pub main_inventory: [Arc>; Self::MAIN_SIZE], - pub equipment_slots: HashMap, + pub equipment_slots: Arc>, selected_slot: AtomicU8, pub entity_equipment: Arc>, } impl PlayerInventory { - const MAIN_SIZE: usize = 36; + pub const MAIN_SIZE: usize = 36; const HOTBAR_SIZE: usize = 9; - const OFF_HAND_SLOT: usize = 40; + pub const OFF_HAND_SLOT: usize = 40; // TODO: Add inventory load from nbt - pub fn new(entity_equipment: Arc>) -> Self { + pub fn new( + entity_equipment: Arc>, + equipment_slots: Arc>, + ) -> Self { Self { // Normal syntax can't be used here because Arc doesn't implement Copy main_inventory: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), - equipment_slots: Self::build_equipment_slots(), + equipment_slots, selected_slot: AtomicU8::new(0), entity_equipment, } @@ -45,6 +49,13 @@ impl PlayerInventory { .clone() } + pub async fn get_stack_in_hand(&self, hand: Hand) -> Arc> { + match hand { + Hand::Left => self.off_hand_item().await, + Hand::Right => self.held_item(), + } + } + /// getOffHandStack in source pub async fn off_hand_item(&self) -> Arc> { let slot = self @@ -71,28 +82,6 @@ impl PlayerInventory { slot < Self::HOTBAR_SIZE } - fn build_equipment_slots() -> HashMap { - let mut equipment_slots = HashMap::new(); - equipment_slots.insert( - EquipmentSlot::FEET.get_offset_entity_slot_id(Self::MAIN_SIZE as i32) as usize, - EquipmentSlot::FEET, - ); - equipment_slots.insert( - EquipmentSlot::LEGS.get_offset_entity_slot_id(Self::MAIN_SIZE as i32) as usize, - EquipmentSlot::LEGS, - ); - equipment_slots.insert( - EquipmentSlot::CHEST.get_offset_entity_slot_id(Self::MAIN_SIZE as i32) as usize, - EquipmentSlot::CHEST, - ); - equipment_slots.insert( - EquipmentSlot::HEAD.get_offset_entity_slot_id(Self::MAIN_SIZE as i32) as usize, - EquipmentSlot::HEAD, - ); - equipment_slots.insert(PlayerInventory::OFF_HAND_SLOT, EquipmentSlot::OFF_HAND); - equipment_slots - } - async fn add_stack(&self, stack: ItemStack) -> usize { let mut slot_index = self.get_occupied_slot_with_room_for_stack(&stack).await; diff --git a/pumpkin-registry/src/biome.rs b/pumpkin-registry/src/biome.rs index f5d419e0f..74546bb98 100644 --- a/pumpkin-registry/src/biome.rs +++ b/pumpkin-registry/src/biome.rs @@ -61,10 +61,10 @@ struct AdditionsSound { tick_chance: f64, } -#[derive(Debug, Clone, Serialize, Deserialize)] -struct Music { - sound: String, - min_delay: i32, - max_delay: i32, - replace_current_music: bool, -} +// #[derive(Debug, Clone, Serialize, Deserialize)] +// struct Music { +// sound: String, +// min_delay: i32, +// max_delay: i32, +// replace_current_music: bool, +// } diff --git a/pumpkin-registry/src/lib.rs b/pumpkin-registry/src/lib.rs index 9aa920995..728a7d9b2 100644 --- a/pumpkin-registry/src/lib.rs +++ b/pumpkin-registry/src/lib.rs @@ -73,12 +73,6 @@ pub struct SyncedRegistry { instrument: IndexMap, } -#[derive(Debug, Clone, Serialize, Deserialize)] -struct DataPool { - data: T, - weight: i32, -} - // TODO: remove in favor of numerical registry ids for `minecraft:dimension_type` #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum VanillaDimensionType { diff --git a/pumpkin-util/src/lib.rs b/pumpkin-util/src/lib.rs index 8230d2419..c916d6ac1 100644 --- a/pumpkin-util/src/lib.rs +++ b/pumpkin-util/src/lib.rs @@ -140,3 +140,32 @@ macro_rules! assert_eq_delta { } }; } + +/// Represents the player's dominant hand. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Hand { + /// Usually the player's off-hand. + Left, + /// Usually the player's primary hand. + Right, +} + +impl Hand { + pub fn all() -> [Self; 2] { + [Self::Right, Self::Left] + } +} + +pub struct InvalidHand; + +impl TryFrom for Hand { + type Error = InvalidHand; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(Self::Left), + 1 => Ok(Self::Right), + _ => Err(InvalidHand), + } + } +} diff --git a/pumpkin/src/block/blocks/cactus.rs b/pumpkin/src/block/blocks/cactus.rs index 83eb473c1..ed441f6d9 100644 --- a/pumpkin/src/block/blocks/cactus.rs +++ b/pumpkin/src/block/blocks/cactus.rs @@ -2,7 +2,6 @@ use async_trait::async_trait; use pumpkin_data::block_properties::{ BlockProperties, CactusLikeProperties, EnumVariants, Integer0To15, }; -use pumpkin_data::damage::DamageType; use pumpkin_data::tag::Taggable; use pumpkin_data::{Block, BlockDirection, tag}; use pumpkin_macros::pumpkin_block; @@ -68,8 +67,9 @@ impl BlockBehaviour for CactusBlock { } } - async fn on_entity_collision(&self, args: OnEntityCollisionArgs<'_>) { - args.entity.damage(1.0, DamageType::CACTUS).await; + async fn on_entity_collision(&self, _args: OnEntityCollisionArgs<'_>) { + // TODO + //args.entity.damage(1.0, DamageType::CACTUS).await; } async fn get_state_for_neighbor_update( diff --git a/pumpkin/src/block/blocks/campfire.rs b/pumpkin/src/block/blocks/campfire.rs index b433fb2fb..15fa6cda5 100644 --- a/pumpkin/src/block/blocks/campfire.rs +++ b/pumpkin/src/block/blocks/campfire.rs @@ -2,7 +2,6 @@ use async_trait::async_trait; use pumpkin_data::{ Block, BlockDirection, block_properties::{BlockProperties, CampfireLikeProperties}, - damage::DamageType, fluid::Fluid, }; use pumpkin_world::{BlockStateId, tick::TickPriority}; @@ -37,7 +36,8 @@ impl BlockBehaviour for CampfireBlock { if CampfireLikeProperties::from_state_id(args.state.id, args.block).lit && args.entity.get_living_entity().is_some() { - args.entity.damage(1.0, DamageType::CAMPFIRE).await; + // TODO + //args.entity.damage(args.entity, 1.0, DamageType::CAMPFIRE).await; } } diff --git a/pumpkin/src/command/args/command.rs b/pumpkin/src/command/args/command.rs index a6ed27c4c..587f65e72 100644 --- a/pumpkin/src/command/args/command.rs +++ b/pumpkin/src/command/args/command.rs @@ -58,7 +58,7 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer { .commands .keys() .filter(|suggestion| suggestion.starts_with(input)) - .map(|suggestion| CommandSuggestion::new(suggestion.to_string(), None)) + .map(|suggestion| CommandSuggestion::new(suggestion.clone(), None)) .collect(); Ok(Some(suggestions)) } diff --git a/pumpkin/src/command/commands/ban.rs b/pumpkin/src/command/commands/ban.rs index c6fd21da9..527a18270 100644 --- a/pumpkin/src/command/commands/ban.rs +++ b/pumpkin/src/command/commands/ban.rs @@ -59,7 +59,7 @@ impl CommandExecutor for ReasonExecutor { return Err(InvalidConsumption(Some(ARG_REASON.into()))); }; - ban_player(sender, &targets[0], Some(reason.to_string())).await; + ban_player(sender, &targets[0], Some(reason.clone())).await; Ok(()) } } diff --git a/pumpkin/src/command/commands/banip.rs b/pumpkin/src/command/commands/banip.rs index 9d52897ee..791a743c1 100644 --- a/pumpkin/src/command/commands/banip.rs +++ b/pumpkin/src/command/commands/banip.rs @@ -72,7 +72,7 @@ impl CommandExecutor for ReasonExecutor { return Err(InvalidConsumption(Some(ARG_REASON.into()))); }; - ban_ip(sender, server, target, Some(reason.to_string())).await; + ban_ip(sender, server, target, Some(reason.clone())).await; Ok(()) } } diff --git a/pumpkin/src/command/commands/bossbar.rs b/pumpkin/src/command/commands/bossbar.rs index 87ef3e642..3c8b2e95d 100644 --- a/pumpkin/src/command/commands/bossbar.rs +++ b/pumpkin/src/command/commands/bossbar.rs @@ -77,7 +77,7 @@ impl CommandExecutor for AddExecuter { sender, TextComponent::translate( "commands.bossbar.create.failed", - [TextComponent::text(namespace.to_string())], + [TextComponent::text(namespace.clone())], ), ) .await; @@ -89,12 +89,12 @@ impl CommandExecutor for AddExecuter { .bossbars .lock() .await - .create_bossbar(namespace.to_string(), bossbar.clone()); + .create_bossbar(namespace.clone(), bossbar.clone()); sender .send_message(TextComponent::translate( "commands.bossbar.create.success", - [bossbar_prefix(bossbar.title.clone(), namespace.to_string())], + [bossbar_prefix(bossbar.title.clone(), namespace.clone())], )) .await; @@ -119,7 +119,7 @@ impl CommandExecutor for GetExecuter { let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else { handle_bossbar_error( sender, - BossbarUpdateError::InvalidResourceLocation(namespace.to_string()), + BossbarUpdateError::InvalidResourceLocation(namespace.clone()), ) .await; return Ok(()); @@ -131,10 +131,7 @@ impl CommandExecutor for GetExecuter { .send_message(TextComponent::translate( "commands.bossbar.get.max", [ - bossbar_prefix( - bossbar.bossbar_data.title.clone(), - namespace.to_string(), - ), + bossbar_prefix(bossbar.bossbar_data.title.clone(), namespace.clone()), TextComponent::text(bossbar.max.to_string()), ], )) @@ -147,10 +144,7 @@ impl CommandExecutor for GetExecuter { .send_message(TextComponent::translate( "commands.bossbar.get.value", [ - bossbar_prefix( - bossbar.bossbar_data.title.clone(), - namespace.to_string(), - ), + bossbar_prefix(bossbar.bossbar_data.title.clone(), namespace.clone()), TextComponent::text(bossbar.value.to_string()), ], )) @@ -168,7 +162,7 @@ impl CommandExecutor for GetExecuter { state, [bossbar_prefix( bossbar.bossbar_data.title.clone(), - namespace.to_string(), + namespace.clone(), )], )) .await; @@ -215,13 +209,13 @@ impl CommandExecutor for ListExecuter { if i == 0 { bossbars_text = bossbars_text.add_child(bossbar_prefix( bossbar.bossbar_data.title.clone(), - bossbar.namespace.to_string(), + bossbar.namespace.clone(), )); } else { bossbars_text = bossbars_text.add_child(TextComponent::text(", ").add_child(bossbar_prefix( bossbar.bossbar_data.title.clone(), - bossbar.namespace.to_string(), + bossbar.namespace.clone(), ))); } } @@ -267,7 +261,7 @@ impl CommandExecutor for RemoveExecuter { "commands.bossbar.remove.success", [bossbar_prefix( bossbar.bossbar_data.title.clone(), - namespace.to_string(), + namespace.clone(), )], )) .await; @@ -276,7 +270,7 @@ impl CommandExecutor for RemoveExecuter { .bossbars .lock() .await - .remove_bossbar(server, namespace.to_string()) + .remove_bossbar(server, namespace.clone()) .await { Ok(()) => {} @@ -470,7 +464,7 @@ impl CommandExecutor for SetExecuter { namespace.to_string(), ), TextComponent::text(count.to_string()), - TextComponent::text(player_names.join(", ").to_string()), + TextComponent::text(player_names.join(", ").clone()), ], )) .await; diff --git a/pumpkin/src/command/commands/damage.rs b/pumpkin/src/command/commands/damage.rs index 93b0912c4..fc81894d7 100644 --- a/pumpkin/src/command/commands/damage.rs +++ b/pumpkin/src/command/commands/damage.rs @@ -89,7 +89,14 @@ impl CommandExecutor for LocationExecutor { let location = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; let success = target - .damage_with_context(amount, damage_type, Some(location), None, None) + .damage_with_context( + target.clone(), + amount, + damage_type, + Some(location), + None, + None, + ) .await; send_damage_result(sender, success, amount, target.get_display_name().await).await; @@ -134,6 +141,7 @@ impl CommandExecutor for EntityExecutor { let success = target .damage_with_context( + target.clone(), amount, damage_type, None, diff --git a/pumpkin/src/command/commands/data.rs b/pumpkin/src/command/commands/data.rs index 117968cdf..dd4772c2e 100644 --- a/pumpkin/src/command/commands/data.rs +++ b/pumpkin/src/command/commands/data.rs @@ -154,9 +154,7 @@ pub fn snbt_colorful_display(tag: &NbtTag, depth: usize) -> Result, ) -> Result<(), CommandError> { let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?; - target.kill().await; + target.kill(target.clone()).await; sender .send_message(TextComponent::translate( diff --git a/pumpkin/src/command/dispatcher.rs b/pumpkin/src/command/dispatcher.rs index 92fc92c60..52dac1065 100644 --- a/pumpkin/src/command/dispatcher.rs +++ b/pumpkin/src/command/dispatcher.rs @@ -411,15 +411,13 @@ impl CommandDispatcher { for name in names { self.commands - .insert(name.to_string(), Command::Alias(primary_name.to_string())); - self.permissions - .insert(name.to_string(), permission.clone()); + .insert(name.clone(), Command::Alias(primary_name.clone())); + self.permissions.insert(name.clone(), permission.clone()); } - self.permissions - .insert(primary_name.to_string(), permission); + self.permissions.insert(primary_name.clone(), permission); self.commands - .insert(primary_name.to_string(), Command::Tree(tree)); + .insert(primary_name.clone(), Command::Tree(tree)); } /// Remove a command from the dispatcher by its primary name. diff --git a/pumpkin/src/entity/decoration/painting.rs b/pumpkin/src/entity/decoration/painting.rs index 734caef60..580b346ad 100644 --- a/pumpkin/src/entity/decoration/painting.rs +++ b/pumpkin/src/entity/decoration/painting.rs @@ -1,5 +1,5 @@ use core::f32; -use std::sync::atomic::Ordering; +use std::sync::{Arc, atomic::Ordering}; use crate::entity::{Entity, EntityBase, NBTStorage, living::LivingEntity}; use async_trait::async_trait; @@ -41,6 +41,7 @@ impl EntityBase for PaintingEntity { async fn damage_with_context( &self, + _caller: Arc, _amount: f32, _damage_type: DamageType, _position: Option>, diff --git a/pumpkin/src/entity/experience_orb.rs b/pumpkin/src/entity/experience_orb.rs index a8cc43807..aa7e1a062 100644 --- a/pumpkin/src/entity/experience_orb.rs +++ b/pumpkin/src/entity/experience_orb.rs @@ -5,7 +5,7 @@ use std::sync::{ }; use async_trait::async_trait; -use pumpkin_data::{damage::DamageType, entity::EntityType}; +use pumpkin_data::entity::EntityType; use pumpkin_util::math::vector3::Vector3; use uuid::Uuid; @@ -125,17 +125,6 @@ impl EntityBase for ExperienceOrbEntity { } } - async fn damage_with_context( - &self, - _amount: f32, - _damage_type: DamageType, - _position: Option>, - _source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - false - } - fn get_living_entity(&self) -> Option<&LivingEntity> { None } diff --git a/pumpkin/src/entity/falling.rs b/pumpkin/src/entity/falling.rs index a377fe4e5..1d4bec11c 100644 --- a/pumpkin/src/entity/falling.rs +++ b/pumpkin/src/entity/falling.rs @@ -1,8 +1,8 @@ use async_trait::async_trait; use pumpkin_data::Block; -use pumpkin_data::{damage::DamageType, entity::EntityType}; +use pumpkin_data::{entity::EntityType}; use pumpkin_protocol::java::client::play::{MetaDataType, Metadata}; -use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; +use pumpkin_util::math::{position::BlockPos}; use pumpkin_world::{BlockStateId, world::BlockFlags}; use std::sync::{Arc, atomic::Ordering}; use uuid::Uuid; @@ -97,17 +97,6 @@ impl EntityBase for FallingEntity { .await; } - async fn damage_with_context( - &self, - _amount: f32, - _damage_type: DamageType, - _position: Option>, - _source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - false - } - fn get_entity(&self) -> &Entity { &self.entity } diff --git a/pumpkin/src/entity/hunger.rs b/pumpkin/src/entity/hunger.rs index 571aa7d7e..2a0d1e370 100644 --- a/pumpkin/src/entity/hunger.rs +++ b/pumpkin/src/entity/hunger.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use super::{EntityBase, NBTStorage, NBTStorageInit, player::Player}; use async_trait::async_trait; use crossbeam::atomic::AtomicCell; @@ -27,7 +29,7 @@ impl Default for HungerManager { } impl HungerManager { - pub async fn tick(&self, player: &Player) { + pub async fn tick(&self, player: &Arc) { let saturation = self.saturation.load(); let level = self.level.load(); let exhaustion = self.exhaustion.load(); @@ -68,7 +70,7 @@ impl HungerManager { || (difficulty == Difficulty::Hard) || (health > 1.0 && difficulty == Difficulty::Normal) { - player.damage(1.0, DamageType::STARVE).await; + player.damage(player.clone(), 1.0, DamageType::STARVE).await; } self.tick_timer.store(0); } diff --git a/pumpkin/src/entity/item.rs b/pumpkin/src/entity/item.rs index 7977a4b1b..25af5806e 100644 --- a/pumpkin/src/entity/item.rs +++ b/pumpkin/src/entity/item.rs @@ -312,7 +312,7 @@ impl EntityBase for ItemEntity { 2 }; - if age % n == 0 && self.can_merge().await { + if age.is_multiple_of(n) && self.can_merge().await { self.try_merge().await; } } @@ -352,6 +352,7 @@ impl EntityBase for ItemEntity { async fn damage_with_context( &self, + _caller: Arc, amount: f32, _damage_type: DamageType, _position: Option>, @@ -366,7 +367,12 @@ impl EntityBase for ItemEntity { true } - async fn damage(&self, _amount: f32, _damage_type: DamageType) -> bool { + async fn damage( + &self, + _caller: Arc, + _amount: f32, + _damage_type: DamageType, + ) -> bool { false } diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs index 77b6a7825..8ece63150 100644 --- a/pumpkin/src/entity/living.rs +++ b/pumpkin/src/entity/living.rs @@ -1,4 +1,7 @@ use pumpkin_data::potion::Effect; +use pumpkin_inventory::build_equipment_slots; +use pumpkin_inventory::player::player_inventory::PlayerInventory; +use pumpkin_util::Hand; use pumpkin_util::math::position::BlockPos; use std::sync::Arc; use std::sync::atomic::Ordering; @@ -10,18 +13,17 @@ use std::{collections::HashMap, sync::atomic::AtomicI32}; use super::{Entity, NBTStorage}; use super::{EntityBase, NBTStorageInit}; -use crate::entity::player::Hand; use crate::server::Server; use crate::world::loot::{LootContextParameters, LootTableExt}; use async_trait::async_trait; use crossbeam::atomic::AtomicCell; use pumpkin_config::advanced_config; -use pumpkin_data::Block; use pumpkin_data::damage::DeathMessageType; -use pumpkin_data::data_component_impl::{EquipmentSlot, FoodImpl}; +use pumpkin_data::data_component_impl::{DeathProtectionImpl, EquipmentSlot, FoodImpl}; use pumpkin_data::effect::StatusEffect; use pumpkin_data::entity::{EntityPose, EntityStatus, EntityType}; use pumpkin_data::sound::SoundCategory; +use pumpkin_data::Block; use pumpkin_data::{damage::DamageType, sound::Sound}; use pumpkin_inventory::entity_equipment::EntityEquipment; use pumpkin_nbt::compound::NbtCompound; @@ -59,6 +61,7 @@ pub struct LivingEntity { pub active_effects: Mutex>, pub entity_equipment: Arc>, pub movement_input: AtomicCell>, + pub equipment_slots: Arc>, pub movement_speed: AtomicCell, @@ -112,6 +115,7 @@ impl LivingEntity { livings_flags: AtomicU8::new(0), active_effects: Mutex::new(HashMap::new()), entity_equipment: Arc::new(Mutex::new(EntityEquipment::new())), + equipment_slots: Arc::new(build_equipment_slots()), jumping: AtomicBool::new(false), jumping_cooldown: AtomicU8::new(0), climbing: AtomicBool::new(false), @@ -336,7 +340,7 @@ impl LivingEntity { let suffocating = self.entity.tick_block_collisions(&caller, server).await; if suffocating { - self.damage(1.0, DamageType::IN_WALL).await; + self.damage(caller, 1.0, DamageType::IN_WALL).await; } } @@ -680,6 +684,7 @@ impl LivingEntity { pub async fn update_fall_distance( &self, + caller: Arc, height_difference: f64, ground: bool, dont_damage: bool, @@ -700,7 +705,7 @@ impl LivingEntity { // TODO: Play block fall sound if damage > 0.0 { - let check_damage = self.damage(damage, DamageType::FALL).await; // Fall + let check_damage = self.damage(caller, damage, DamageType::FALL).await; // Fall if check_damage { self.entity .play_sound(Self::get_fall_sound(fall_distance as i32)) @@ -843,6 +848,56 @@ impl LivingEntity { } } + async fn try_use_death_protector(&self, caller: &Arc) -> bool { + for hand in Hand::all() { + let stack = self.get_stack_in_hand(caller, hand).await; + let mut stack = stack.lock().await; + // TODO: effects... + if stack.get_data_component::().is_some() { + stack.decrement(1); + self.set_health(1.0).await; + self.entity + .world + .send_entity_status(&self.entity, EntityStatus::UseTotemOfUndying) + .await; + return true; + } + } + + false + } + + pub async fn held_item(&self, caller: &Arc) -> Arc> { + if let Some(player) = caller.get_player() { + return player.inventory.held_item(); + } + let slot = self + .equipment_slots + .get(&PlayerInventory::OFF_HAND_SLOT) + .unwrap(); + self.entity_equipment.lock().await.get(slot) + } + + pub async fn get_stack_in_hand( + &self, + caller: &Arc, + hand: Hand, + ) -> Arc> { + match hand { + Hand::Left => self.off_hand_item().await, + Hand::Right => self.held_item(caller).await, + } + } + + /// getOffHandStack in source + pub async fn off_hand_item(&self) -> Arc> { + let slot = self + .equipment_slots + .get(&PlayerInventory::OFF_HAND_SLOT) + .unwrap(); + self.entity_equipment.lock().await.get(slot) + } + pub fn is_part_of_game(&self) -> bool { self.is_spectator() && self.entity.is_alive() } @@ -914,6 +969,7 @@ impl NBTStorage for LivingEntity { impl EntityBase for LivingEntity { async fn damage_with_context( &self, + caller: Arc, amount: f32, damage_type: DamageType, position: Option>, @@ -996,7 +1052,7 @@ impl EntityBase for LivingEntity { self.set_health(new_health).await; } - if new_health <= 0.0 { + if new_health <= 0.0 && !self.try_use_death_protector(&caller).await { self.on_death(damage_type, source, cause).await; } diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs index ae5c78b3b..bd6fe33f0 100644 --- a/pumpkin/src/entity/mob/mod.rs +++ b/pumpkin/src/entity/mob/mod.rs @@ -113,6 +113,7 @@ where async fn damage_with_context( &self, + caller: Arc, amount: f32, damage_type: DamageType, position: Option>, @@ -121,7 +122,7 @@ where ) -> bool { self.get_mob_entity() .living_entity - .damage_with_context(amount, damage_type, position, source, cause) + .damage_with_context(caller, amount, damage_type, position, source, cause) .await } diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 17e6b1df2..4af21143d 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -106,8 +106,13 @@ pub trait EntityBase: Send + Sync + NBTStorage { } /// Returns if damage was successful or not - async fn damage(&self, amount: f32, damage_type: DamageType) -> bool { - self.damage_with_context(amount, damage_type, None, None, None) + async fn damage( + &self, + caller: Arc, + amount: f32, + damage_type: DamageType, + ) -> bool { + self.damage_with_context(caller, amount, damage_type, None, None, None) .await } @@ -129,12 +134,16 @@ pub trait EntityBase: Send + Sync + NBTStorage { async fn damage_with_context( &self, - amount: f32, - damage_type: DamageType, - position: Option>, - source: Option<&dyn EntityBase>, - cause: Option<&dyn EntityBase>, - ) -> bool; + _caller: Arc, + _amount: f32, + _damage_type: DamageType, + _position: Option>, + _source: Option<&dyn EntityBase>, + _cause: Option<&dyn EntityBase>, + ) -> bool { + // Just do nothing + false + } /// Called when a player collides with a entity async fn on_player_collision(&self, _player: &Arc) {} @@ -181,9 +190,11 @@ pub trait EntityBase: Send + Sync + NBTStorage { } /// Kills the Entity. - async fn kill(&self) { + async fn kill(&self, caller: Arc) { if let Some(living) = self.get_living_entity() { - living.damage(f32::MAX, DamageType::GENERIC_KILL).await; + living + .damage(caller, f32::MAX, DamageType::GENERIC_KILL) + .await; } else { // TODO this should be removed once all entities are implemented self.get_entity().remove().await; @@ -1174,7 +1185,12 @@ impl Entity { if let Some(living) = caller.get_living_entity() { living - .update_fall_distance(final_move.y, self.on_ground.load(Ordering::SeqCst), false) + .update_fall_distance( + caller.clone(), + final_move.y, + self.on_ground.load(Ordering::SeqCst), + false, + ) .await; } } @@ -1694,10 +1710,12 @@ impl Entity { vehicle.is_some() } - pub async fn check_out_of_world(&self, dyn_self: &dyn EntityBase) { + pub async fn check_out_of_world(&self, dyn_self: Arc) { if self.pos.load().y < f64::from(self.world.generation_settings().shape.min_y) - 64.0 { // Tick out of world damage - dyn_self.damage(4.0, DamageType::OUT_OF_WORLD).await; + dyn_self + .damage(dyn_self.clone(), 4.0, DamageType::OUT_OF_WORLD) + .await; } } @@ -1792,21 +1810,10 @@ impl NBTStorage for Entity { #[async_trait] impl EntityBase for Entity { - async fn damage_with_context( - &self, - _amount: f32, - _damage_type: DamageType, - _position: Option>, - _source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - false - } - async fn tick(&self, caller: Arc, _server: &Server) { self.tick_portal(&caller).await; self.update_fluid_state(&caller).await; - self.check_out_of_world(&*caller).await; + self.check_out_of_world(caller.clone()).await; let fire_ticks = self.fire_ticks.load(Ordering::Relaxed); if fire_ticks > 0 { if self.entity_type.fire_immune { @@ -1816,7 +1823,9 @@ impl EntityBase for Entity { } } else { if fire_ticks % 20 == 0 { - caller.damage(1.0, DamageType::ON_FIRE).await; + caller + .damage(caller.clone(), 1.0, DamageType::ON_FIRE) + .await; } self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed); diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index b620636e9..2925b83dc 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -53,7 +53,6 @@ use pumpkin_protocol::java::client::play::{ }; use pumpkin_protocol::java::server::play::SClickSlot; use pumpkin_registry::VanillaDimensionType; -use pumpkin_util::GameMode; use pumpkin_util::math::{ boundingbox::BoundingBox, experience, position::BlockPos, vector2::Vector2, vector3::Vector3, }; @@ -62,6 +61,7 @@ use pumpkin_util::resource_location::ResourceLocation; use pumpkin_util::text::TextComponent; use pumpkin_util::text::click::ClickEvent; use pumpkin_util::text::hover::HoverEvent; +use pumpkin_util::{GameMode, Hand}; use pumpkin_world::biome; use pumpkin_world::cylindrical_chunk_iterator::Cylindrical; use pumpkin_world::entity::entity_data_flags::{ @@ -308,7 +308,10 @@ impl Player { matches!(gamemode, GameMode::Creative | GameMode::Spectator), )); - let inventory = Arc::new(PlayerInventory::new(living_entity.entity_equipment.clone())); + let inventory = Arc::new(PlayerInventory::new( + living_entity.entity_equipment.clone(), + living_entity.equipment_slots.clone(), + )); let player_screen_handler = Arc::new(Mutex::new( PlayerScreenHandler::new(&inventory, None, 0).await, @@ -492,6 +495,7 @@ impl Player { if !victim .damage_with_context( + victim.clone(), damage as f32, DamageType::PLAYER_ATTACK, None, @@ -829,7 +833,7 @@ impl Player { self.last_attacked_ticks.fetch_add(1, Ordering::Relaxed); self.living_entity.tick(self.clone(), server).await; - self.hunger_manager.tick(self.as_ref()).await; + self.hunger_manager.tick(self).await; // experience handling self.tick_experience().await; @@ -2052,6 +2056,7 @@ impl NBTStorageInit for PlayerInventory {} impl EntityBase for Player { async fn damage_with_context( &self, + caller: Arc, amount: f32, damage_type: DamageType, position: Option>, @@ -2070,7 +2075,7 @@ impl EntityBase for Player { .expect("Entity not found in world"); let result = self .living_entity - .damage_with_context(amount, damage_type, position, source, cause) + .damage_with_context(caller, amount, damage_type, position, source, cause) .await; if result { let health = self.living_entity.health.load(); @@ -2258,29 +2263,6 @@ impl Abilities { } } -/// Represents the player's dominant hand. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Hand { - /// Usually the player's off-hand. - Left, - /// Usually the player's primary hand. - Right, -} - -pub struct InvalidHand; - -impl TryFrom for Hand { - type Error = InvalidHand; - - fn try_from(value: i32) -> Result { - match value { - 0 => Ok(Self::Left), - 1 => Ok(Self::Right), - _ => Err(InvalidHand), - } - } -} - /// Represents the player's respawn point. #[derive(Copy, Debug, Clone, PartialEq)] pub struct RespawnPoint { diff --git a/pumpkin/src/entity/projectile/mod.rs b/pumpkin/src/entity/projectile/mod.rs index f624183f6..15087b56b 100644 --- a/pumpkin/src/entity/projectile/mod.rs +++ b/pumpkin/src/entity/projectile/mod.rs @@ -5,7 +5,6 @@ use std::{ use super::{Entity, EntityBase, NBTStorage, living::LivingEntity}; use async_trait::async_trait; -use pumpkin_data::damage::DamageType; use pumpkin_util::math::vector3::Vector3; pub struct ThrownItemEntity { @@ -85,17 +84,6 @@ impl EntityBase for ThrownItemEntity { &self.entity } - async fn damage_with_context( - &self, - _amount: f32, - _damage_type: DamageType, - _position: Option>, - _source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - false - } - fn get_living_entity(&self) -> Option<&LivingEntity> { None } diff --git a/pumpkin/src/entity/tnt.rs b/pumpkin/src/entity/tnt.rs index b53f53426..495d2b8af 100644 --- a/pumpkin/src/entity/tnt.rs +++ b/pumpkin/src/entity/tnt.rs @@ -2,7 +2,7 @@ use super::{Entity, EntityBase, NBTStorage, living::LivingEntity}; use crate::server::Server; use async_trait::async_trait; use core::f32; -use pumpkin_data::{Block, damage::DamageType}; +use pumpkin_data::{Block}; use pumpkin_protocol::{ codec::var_int::VarInt, java::client::play::{MetaDataType, Metadata}, @@ -96,17 +96,6 @@ impl EntityBase for TNTEntity { .await; } - async fn damage_with_context( - &self, - _amount: f32, - _damage_type: DamageType, - _position: Option>, - _source: Option<&dyn EntityBase>, - _cause: Option<&dyn EntityBase>, - ) -> bool { - false - } - fn get_entity(&self) -> &Entity { &self.entity } diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index e9f7f0f56..365ec6650 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -18,7 +18,6 @@ #![deny(clippy::needless_pass_by_ref_mut)] #![deny(clippy::needless_collect)] #![deny(clippy::redundant_clone)] -#![deny(clippy::branches_sharing_code)] #![deny(clippy::set_contains_or_insert)] #![deny(clippy::significant_drop_in_scrutinee)] // use log crate diff --git a/pumpkin/src/net/java/config.rs b/pumpkin/src/net/java/config.rs index bb307ea67..bd0ce0a2e 100644 --- a/pumpkin/src/net/java/config.rs +++ b/pumpkin/src/net/java/config.rs @@ -1,7 +1,7 @@ use std::{num::NonZeroU8, sync::Arc}; use crate::{ - entity::player::{ChatMode, Hand}, + entity::player::ChatMode, net::{ClientPlatform, PlayerConfig, can_not_join, java::JavaClient}, server::Server, }; @@ -15,7 +15,7 @@ use pumpkin_protocol::{ SConfigResourcePack, SKnownPacks, SPluginMessage, }, }; -use pumpkin_util::text::TextComponent; +use pumpkin_util::{Hand, text::TextComponent}; impl JavaClient { pub async fn handle_client_information_config( diff --git a/pumpkin/src/net/java/play.rs b/pumpkin/src/net/java/play.rs index d22f1c9bf..d093ba9ec 100644 --- a/pumpkin/src/net/java/play.rs +++ b/pumpkin/src/net/java/play.rs @@ -1,5 +1,5 @@ use pumpkin_protocol::bedrock::server::text::SText; -use pumpkin_util::PermissionLvl; +use pumpkin_util::{Hand, PermissionLvl}; use rsa::pkcs1v15::{Signature as RsaPkcs1v15Signature, VerifyingKey}; use rsa::signature::Verifier; use sha1::Sha1; @@ -14,7 +14,7 @@ use crate::block::registry::BlockActionResult; use crate::block::{self, BlockIsReplacing}; use crate::command::CommandSender; use crate::entity::EntityBase; -use crate::entity::player::{ChatMode, ChatSession, Hand, Player}; +use crate::entity::player::{ChatMode, ChatSession, Player}; use crate::entity::r#type::from_type; use crate::error::PumpkinError; use crate::net::PlayerConfig; @@ -327,6 +327,7 @@ impl JavaClient { if !player.abilities.lock().await.flying { player.living_entity .update_fall_distance( + player.clone(), height_difference, packet.collision & FLAG_ON_GROUND != 0, player.gamemode.load() == GameMode::Creative, @@ -451,6 +452,7 @@ impl JavaClient { if !player.abilities.lock().await.flying { player.living_entity .update_fall_distance( + player.clone(), height_difference, (packet.collision & FLAG_ON_GROUND) != 0, player.gamemode.load() == GameMode::Creative, diff --git a/pumpkin/src/net/mod.rs b/pumpkin/src/net/mod.rs index e72c67da8..0bcdada69 100644 --- a/pumpkin/src/net/mod.rs +++ b/pumpkin/src/net/mod.rs @@ -9,13 +9,13 @@ use crate::{ banned_ip_data::BANNED_IP_LIST, banned_player_data::BANNED_PLAYER_LIST, op_data::OPERATOR_CONFIG, whitelist_data::WHITELIST_CONFIG, }, - entity::player::{ChatMode, Hand}, + entity::player::ChatMode, net::{bedrock::BedrockClient, java::JavaClient}, server::Server, }; use pumpkin_protocol::{ClientPacket, Property}; -use pumpkin_util::{ProfileAction, text::TextComponent}; +use pumpkin_util::{Hand, ProfileAction, text::TextComponent}; use serde::Deserialize; use sha1::Digest; use sha2::Sha256;