diff --git a/pumpkin/src/block/blocks/tnt.rs b/pumpkin/src/block/blocks/tnt.rs index 42e6779d7..ff516345c 100644 --- a/pumpkin/src/block/blocks/tnt.rs +++ b/pumpkin/src/block/blocks/tnt.rs @@ -9,6 +9,7 @@ use crate::world::World; use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_data::item::Item; +use pumpkin_data::sound::SoundCategory; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::registry::Block; @@ -34,11 +35,19 @@ impl PumpkinBlock for TNTBlock { return BlockActionResult::Continue; } let world = player.world().await; - world.break_block(server, &location, None, false).await; + world.set_block_state(&location, 0).await; let entity = server.add_entity(location.to_f64(), EntityType::TNT, &world); + let pos = entity.pos.load(); let tnt = Arc::new(TNTEntity::new(entity, DEFAULT_POWER, DEFAULT_FUSE)); world.spawn_entity(tnt.clone()).await; tnt.send_meta_packet().await; + world + .play_sound( + pumpkin_data::sound::Sound::EntityTntPrimed, + SoundCategory::Blocks, + &pos, + ) + .await; BlockActionResult::Consume } async fn explode( diff --git a/pumpkin/src/entity/hunger.rs b/pumpkin/src/entity/hunger.rs index 5fb9d68bb..e1810f5b2 100644 --- a/pumpkin/src/entity/hunger.rs +++ b/pumpkin/src/entity/hunger.rs @@ -1,7 +1,7 @@ use crossbeam::atomic::AtomicCell; use pumpkin_data::damage::DamageType; -use super::player::Player; +use super::{EntityBase, player::Player}; pub struct HungerManager { /// The current hunger level. @@ -50,7 +50,7 @@ impl HungerManager { } else if level == 0 { self.tick_timer.fetch_add(1); if self.tick_timer.load() >= 80 { - player.living_entity.damage(1.0, DamageType::STARVE).await; + player.damage(1.0, DamageType::STARVE).await; self.tick_timer.store(0); } } else { diff --git a/pumpkin/src/entity/item.rs b/pumpkin/src/entity/item.rs index e4751726f..0f84482ea 100644 --- a/pumpkin/src/entity/item.rs +++ b/pumpkin/src/entity/item.rs @@ -1,5 +1,6 @@ use crate::server::Server; use async_trait::async_trait; +use pumpkin_data::damage::DamageType; use pumpkin_protocol::{ client::play::{CTakeItemEntity, MetaDataType, Metadata}, codec::slot::Slot, @@ -53,6 +54,10 @@ impl EntityBase for ItemEntity { self.entity.remove().await; } } + async fn damage(&self, _amount: f32, _damage_type: DamageType) -> bool { + false + } + async fn on_player_collision(&self, player: Arc) { if self.pickup_delay.load(std::sync::atomic::Ordering::Relaxed) == 0 { let mut inv = player.inventory.lock().await; diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs index c02b91456..827db43a2 100644 --- a/pumpkin/src/entity/living.rs +++ b/pumpkin/src/entity/living.rs @@ -1,10 +1,14 @@ use std::{collections::HashMap, sync::atomic::AtomicI32}; +use crate::server::Server; use async_trait::async_trait; use crossbeam::atomic::AtomicCell; +use pumpkin_config::ADVANCED_CONFIG; use pumpkin_data::entity::EffectType; use pumpkin_data::{damage::DamageType, sound::Sound}; use pumpkin_nbt::tag::NbtTag; +use pumpkin_protocol::client::play::CHurtAnimation; +use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::{ client::play::{ CDamageEvent, CEntityStatus, CSetEquipment, EquipmentSlot, MetaDataType, Metadata, @@ -15,6 +19,7 @@ use pumpkin_util::math::vector3::Vector3; use pumpkin_world::item::ItemStack; use tokio::sync::Mutex; +use super::EntityBase; use super::{Entity, EntityId, NBTStorage, effect::Effect}; /// Represents a living entity within the game world. @@ -48,17 +53,6 @@ impl LivingEntity { } } - pub fn tick(&self) { - if self - .time_until_regen - .load(std::sync::atomic::Ordering::Relaxed) - > 0 - { - self.time_until_regen - .fetch_sub(1, std::sync::atomic::Ordering::Relaxed); - } - } - pub async fn send_equipment_changes(&self, equipment: &[(EquipmentSlot, ItemStack)]) { let equipment: Vec<(EquipmentSlot, Slot)> = equipment .iter() @@ -151,11 +145,6 @@ impl LivingEntity { effects.get(&effect).cloned() } - pub async fn damage(&self, amount: f32, damage_type: DamageType) -> bool { - self.damage_with_context(amount, damage_type, None, None, None) - .await - } - /// Returns if the entity was damaged or not pub fn check_damage(&self, amount: f32) -> bool { let regen = self @@ -236,8 +225,54 @@ impl LivingEntity { .await .broadcast_packet_all(&CEntityStatus::new(self.entity.entity_id, 3)) .await; + // TODO: wait + self.entity.remove().await; } } + +#[async_trait] +impl EntityBase for LivingEntity { + async fn tick(&self, _server: &Server) { + if self + .time_until_regen + .load(std::sync::atomic::Ordering::Relaxed) + > 0 + { + self.time_until_regen + .fetch_sub(1, std::sync::atomic::Ordering::Relaxed); + } + } + async fn damage(&self, amount: f32, damage_type: DamageType) -> bool { + let world = self.entity.world.read().await; + if !self.check_damage(amount) { + return false; + } + let config = &ADVANCED_CONFIG.pvp; + + if !self + .damage_with_context(amount, damage_type, None, None, None) + .await + { + return false; + } + + if config.hurt_animation { + let entity_id = VarInt(self.entity.entity_id); + world + .broadcast_packet_all(&CHurtAnimation::new(entity_id, self.entity.yaw.load())) + .await; + } + true + } + fn get_entity(&self) -> &Entity { + &self.entity + } + + fn get_living_entity(&self) -> Option<&LivingEntity> { + Some(self) + } +} + #[async_trait] impl NBTStorage for LivingEntity { async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { diff --git a/pumpkin/src/entity/mob/mod.rs b/pumpkin/src/entity/mob/mod.rs index f253247dc..bc846de9d 100644 --- a/pumpkin/src/entity/mob/mod.rs +++ b/pumpkin/src/entity/mob/mod.rs @@ -24,7 +24,8 @@ pub struct MobEntity { #[async_trait] impl EntityBase for MobEntity { - async fn tick(&self, _: &Server) { + async fn tick(&self, server: &Server) { + self.living_entity.tick(server).await; let mut goals = self.goals.lock().await; for (goal, running) in goals.iter_mut() { if *running { diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 8d1b8ee4f..9f33fb1b4 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -50,8 +50,24 @@ pub type EntityId = i32; #[async_trait] pub trait EntityBase: Send + Sync { /// Gets Called every tick - async fn tick(&self, _server: &Server) {} - /// Called when a player collides with the entity + async fn tick(&self, server: &Server) { + if let Some(living) = self.get_living_entity() { + living.tick(server).await; + } else { + self.get_entity().tick(server).await; + } + } + + /// Returns if damage was successful or not + async fn damage(&self, amount: f32, damage_type: DamageType) -> bool { + if let Some(living) = self.get_living_entity() { + living.damage(amount, damage_type).await + } else { + self.get_entity().damage(amount, damage_type).await + } + } + + /// Called when a player collides with a entity async fn on_player_collision(&self, _player: Arc) {} fn get_entity(&self) -> &Entity; fn get_living_entity(&self) -> Option<&LivingEntity>; @@ -404,6 +420,10 @@ impl Entity { #[async_trait] impl EntityBase for Entity { + async fn damage(&self, _amount: f32, _damage_type: DamageType) -> bool { + false + } + async fn tick(&self, _: &Server) {} fn get_entity(&self) -> &Entity { diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 92357885e..1fd8e50d5 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -25,10 +25,9 @@ use pumpkin_protocol::{ bytebuf::packet::Packet, client::play::{ CAcknowledgeBlockChange, CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus, - CGameEvent, CHurtAnimation, CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, - CPlayerInfoUpdate, CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle, - CSystemChatMessage, CTitleText, CUnloadChunk, CUpdateMobEffect, GameEvent, MetaDataType, - PlayerAction, + CGameEvent, CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, + CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle, CSystemChatMessage, + CTitleText, CUnloadChunk, CUpdateMobEffect, GameEvent, MetaDataType, PlayerAction, }, server::play::{ SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd, @@ -288,7 +287,6 @@ impl Player { pub async fn attack(&self, victim: Arc) { let world = self.world().await; let victim_entity = victim.get_entity(); - let victim_living_entity = victim.get_living_entity(); let attacker_entity = &self.living_entity.entity; let config = &ADVANCED_CONFIG.pvp; @@ -336,56 +334,45 @@ impl Player { let pos = victim_entity.pos.load(); - if let Some(living) = victim_living_entity { - if !living.check_damage(damage as f32) { - world - .play_sound( - Sound::EntityPlayerAttackNodamage, - SoundCategory::Players, - &pos, - ) - .await; - return; - } - } - - world - .play_sound(Sound::EntityPlayerHurt, SoundCategory::Players, &pos) - .await; - let attack_type = AttackType::new(self, attack_cooldown_progress as f32).await; - player_attack_sound(&pos, &world, attack_type).await; - if matches!(attack_type, AttackType::Critical) { damage *= 1.5; } - if let Some(living) = victim_living_entity { - living - .damage(damage as f32, DamageType::PLAYER_ATTACK) - .await; - } - - let mut knockback_strength = 1.0; - match attack_type { - AttackType::Knockback => knockback_strength += 1.0, - AttackType::Sweeping => { - combat::spawn_sweep_particle(attacker_entity, &world, &pos).await; - } - _ => {} - }; - - if config.knockback { - combat::handle_knockback(attacker_entity, &world, victim_entity, knockback_strength) - .await; - } - - if config.hurt_animation { - let entity_id = VarInt(victim_entity.entity_id); + if !victim + .damage(damage as f32, DamageType::PLAYER_ATTACK) + .await + { world - .broadcast_packet_all(&CHurtAnimation::new(entity_id, attacker_entity.yaw.load())) + .play_sound( + Sound::EntityPlayerAttackNodamage, + SoundCategory::Players, + &self.living_entity.entity.pos.load(), + ) .await; + return; + } + + if victim.get_living_entity().is_some() { + let mut knockback_strength = 1.0; + player_attack_sound(&pos, &world, attack_type).await; + match attack_type { + AttackType::Knockback => knockback_strength += 1.0, + AttackType::Sweeping => { + combat::spawn_sweep_particle(attacker_entity, &world, &pos).await; + } + _ => {} + }; + if config.knockback { + combat::handle_knockback( + attacker_entity, + &world, + victim_entity, + knockback_strength, + ) + .await; + } } if config.swing {} @@ -447,7 +434,7 @@ impl Player { self.cancel_tasks.notified().await; } - pub async fn tick(&self) { + pub async fn tick(&self, server: &Server) { if self .client .closed @@ -493,7 +480,7 @@ impl Player { self.last_attacked_ticks .fetch_add(1, std::sync::atomic::Ordering::Relaxed); - self.living_entity.tick(); + self.living_entity.tick(server).await; self.hunger_manager.tick(self).await; // timeout/keep alive handling @@ -1186,6 +1173,18 @@ impl NBTStorage for Player { #[async_trait] impl EntityBase for Player { + async fn damage(&self, amount: f32, damage_type: DamageType) -> bool { + self.world() + .await + .play_sound( + Sound::EntityPlayerHurt, + SoundCategory::Players, + &self.living_entity.entity.pos.load(), + ) + .await; + self.living_entity.damage(amount, damage_type).await + } + fn get_entity(&self) -> &Entity { &self.living_entity.entity } diff --git a/pumpkin/src/entity/projectile/mod.rs b/pumpkin/src/entity/projectile/mod.rs index bc05dbb77..092e5cad3 100644 --- a/pumpkin/src/entity/projectile/mod.rs +++ b/pumpkin/src/entity/projectile/mod.rs @@ -1,5 +1,7 @@ use std::f32::{self}; +use async_trait::async_trait; +use pumpkin_data::damage::DamageType; use pumpkin_util::math::vector3::Vector3; use super::{Entity, EntityBase, living::LivingEntity}; @@ -73,11 +75,16 @@ impl ThrownItemEntity { } } +#[async_trait] impl EntityBase for ThrownItemEntity { fn get_entity(&self) -> &Entity { &self.entity } + async fn damage(&self, _amount: f32, _damage_type: DamageType) -> 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 09a7eeab0..3e57b7ad9 100644 --- a/pumpkin/src/entity/tnt.rs +++ b/pumpkin/src/entity/tnt.rs @@ -1,5 +1,6 @@ use crate::server::Server; use async_trait::async_trait; +use pumpkin_data::damage::DamageType; use pumpkin_macros::block_state; use pumpkin_protocol::{ client::play::{MetaDataType, Metadata}, @@ -62,6 +63,9 @@ impl EntityBase for TNTEntity { .await; } } + async fn damage(&self, _amount: f32, _damage_type: DamageType) -> bool { + false + } fn get_entity(&self) -> &Entity { &self.entity diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 87bdde109..a5cdbc5d4 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -284,7 +284,7 @@ impl World { // player ticks for player in self.players.read().await.values() { - player.tick().await; + player.tick(server).await; } let entities_to_tick: Vec<_> = self.entities.read().await.values().cloned().collect();