diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index bb3a01d1a..bd1cf5db0 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -394,6 +394,7 @@ impl Player { // Also refactor out a better method to get individual advanced state ids let players = self + .living_entity .entity .world .current_players diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index a9a881c0c..10d9e5f8d 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -50,7 +50,9 @@ impl Player { if let Some((id, position)) = awaiting_teleport.as_ref() { if id == &confirm_teleport.teleport_id { // we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported - self.entity.set_pos(position.x, position.y, position.z); + self.living_entity + .entity + .set_pos(position.x, position.y, position.z); *awaiting_teleport = None; } else { @@ -76,7 +78,7 @@ impl Player { self.kick(TextComponent::text("Invalid movement")); return; } - let entity = &self.entity; + let entity = &self.living_entity.entity; entity.set_pos( Self::clamp_horizontal(position.x), Self::clamp_vertical(position.feet_y), @@ -135,7 +137,7 @@ impl Player { self.kick(TextComponent::text("Invalid rotation")); return; } - let entity = &self.entity; + let entity = &self.living_entity.entity; entity.set_pos( Self::clamp_horizontal(position_rotation.x), @@ -200,7 +202,7 @@ impl Player { self.kick(TextComponent::text("Invalid rotation")); return; } - let entity = &self.entity; + let entity = &self.living_entity.entity; entity .on_ground .store(rotation.ground, std::sync::atomic::Ordering::Relaxed); @@ -228,7 +230,8 @@ impl Player { } pub fn handle_player_ground(&self, _server: &Arc, ground: SSetPlayerGround) { - self.entity + self.living_entity + .entity .on_ground .store(ground.on_ground, std::sync::atomic::Ordering::Relaxed); } @@ -239,7 +242,7 @@ impl Player { } if let Some(action) = Action::from_i32(command.action.0) { - let entity = &self.entity; + let entity = &self.living_entity.entity; match action { pumpkin_protocol::server::play::Action::StartSneaking => { if !entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) { @@ -289,7 +292,7 @@ impl Player { Hand::Off => Animation::SwingOffhand, }; let id = self.entity_id(); - let world = &self.entity.world; + let world = &self.living_entity.entity.world; world.broadcast_packet_expect( &[self.client.token], &CEntityAnimation::new(id.into(), animation as u8), @@ -313,7 +316,7 @@ impl Player { // TODO: filter message & validation let gameprofile = &self.gameprofile; - let entity = &self.entity; + let entity = &self.living_entity.entity; let world = &entity.world; world.broadcast_packet_all(&CPlayerChatMessage::new( gameprofile.id, @@ -367,7 +370,7 @@ impl Player { pub async fn handle_interact(&self, _: &Arc, interact: SInteract) { let sneaking = interact.sneaking; - let entity = &self.entity; + let entity = &self.living_entity.entity; if entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking { entity.set_sneaking(sneaking).await; } @@ -381,7 +384,7 @@ impl Player { let world = &entity.world; let attacked_player = world.get_player_by_entityid(entity_id.0 as EntityId); if let Some(player) = attacked_player { - let victem_entity = &player.entity; + let victem_entity = &player.living_entity.entity; if config.protect_creative && player.gamemode.load() == GameMode::Creative { @@ -447,7 +450,7 @@ impl Player { let location = player_action.location; // Block break & block break sound // TODO: currently this is always dirt replace it - let entity = &self.entity; + let entity = &self.living_entity.entity; let world = &entity.world; world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false)); // AIR @@ -471,7 +474,7 @@ impl Player { } // Block break & block break sound // TODO: currently this is always dirt replace it - let entity = &self.entity; + let entity = &self.living_entity.entity; let world = &entity.world; world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false)); // AIR @@ -518,7 +521,7 @@ impl Player { ) .expect("All item ids are in the global registry"); if let Ok(block_state_id) = BlockState::new(minecraft_id, None) { - let entity = &self.entity; + let entity = &self.living_entity.entity; let world = &entity.world; world.broadcast_packet_all(&CBlockUpdate::new( &location, diff --git a/pumpkin/src/commands/cmd_kill.rs b/pumpkin/src/commands/cmd_kill.rs index 3b0bdf3fa..77c95f40e 100644 --- a/pumpkin/src/commands/cmd_kill.rs +++ b/pumpkin/src/commands/cmd_kill.rs @@ -18,7 +18,7 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION).with_child( argument(ARG_TARGET, consume_arg_target).execute(&|sender, server, args| { let target = parse_arg_player(sender, server, ARG_TARGET, args)?; - target.entity.kill(); + target.living_entity.kill(); sender.send_message( TextComponent::text("Player has been killed.").color_named(NamedColor::Blue), diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs new file mode 100644 index 000000000..a6ae33ede --- /dev/null +++ b/pumpkin/src/entity/living.rs @@ -0,0 +1,46 @@ +use crossbeam::atomic::AtomicCell; +use pumpkin_protocol::client::play::{CEntityStatus, CSetEntityMetadata, Metadata}; + +use super::Entity; + +/// Represents a Living Entity (e.g. Player, Zombie, Enderman...) +pub struct LivingEntity { + pub entity: Entity, + /// The entity's current health level. + pub health: AtomicCell, +} + +impl LivingEntity { + pub const fn new(entity: Entity) -> Self { + Self { + entity, + health: AtomicCell::new(20.0), + } + } + + pub fn set_health(&self, health: f32) { + self.health.store(health); + // tell everyone entities health changed + self.entity + .world + .broadcast_packet_all(&CSetEntityMetadata::new( + self.entity.entity_id.into(), + Metadata::new(9, 3.into(), health), + )); + } + + /// Kills the Entity + /// + /// This is similar to `kill` but Spawn Particles, Animation and plays death sound + pub fn kill(&self) { + // Spawns death smoke particles + self.entity + .world + .broadcast_packet_all(&CEntityStatus::new(self.entity.entity_id, 60)); + // Plays the death sound and death animation + self.entity + .world + .broadcast_packet_all(&CEntityStatus::new(self.entity.entity_id, 3)); + self.entity.remove(); + } +} diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 71204dfa7..9ffcbb94a 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -8,14 +8,16 @@ use pumpkin_core::math::{ }; use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, EntityId}; use pumpkin_protocol::{ - client::play::{CEntityStatus, CSetEntityMetadata, Metadata}, + client::play::{CSetEntityMetadata, Metadata}, VarInt, }; use crate::world::World; +pub mod living; pub mod player; +/// Represents a not living Entity (e.g. Item, Egg, Snowball...) pub struct Entity { /// A unique identifier for the entity pub entity_id: EntityId, @@ -24,7 +26,6 @@ pub struct Entity { /// The world in which the entity exists. pub world: Arc, /// The entity's current health level. - pub health: AtomicCell, /// The entity's current position in the world pub pos: AtomicCell>, @@ -75,7 +76,6 @@ impl Entity { sneaking: AtomicBool::new(false), world, // TODO: Load this from previous instance - health: AtomicCell::new(20.0), sprinting: AtomicBool::new(false), fall_flying: AtomicBool::new(false), yaw: AtomicCell::new(0.0), @@ -121,19 +121,6 @@ impl Entity { self.pitch.store(pitch); } - /// Kills the Entity - /// - /// This is similar to `kill` but Spawn Particles, Animation and plays death sound - pub fn kill(&self) { - // Spawns death smoke particles - self.world - .broadcast_packet_all(&CEntityStatus::new(self.entity_id, 60)); - // Plays the death sound and death animation - self.world - .broadcast_packet_all(&CEntityStatus::new(self.entity_id, 3)); - self.remove(); - } - /// Removes the Entity from their current World pub fn remove(&self) { self.world.remove_entity(self); diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 5850da0d8..fc39cdf2b 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -38,15 +38,14 @@ use crate::{ world::World, }; -use super::Entity; +use super::{living::LivingEntity, Entity}; /// Represents a Minecraft player entity. /// /// A `Player` is a special type of entity that represents a human player connected to the server. pub struct Player { - /// The underlying entity object that represents the player. - pub entity: Entity, - + /// The underlying living entity object that represents the player. + pub living_entity: LivingEntity, /// The player's game profile information, including their username and UUID. pub gameprofile: GameProfile, /// The client connection associated with the player. @@ -110,7 +109,12 @@ impl Player { ); let config = client.config.lock().clone().unwrap_or_default(); Self { - entity: Entity::new(entity_id, world, EntityType::Player, 1.62), + living_entity: LivingEntity::new(Entity::new( + entity_id, + world, + EntityType::Player, + 1.62, + )), config: Mutex::new(config), gameprofile, client, @@ -132,11 +136,11 @@ impl Player { /// Removes the Player out of the current World pub async fn remove(&self) { - self.entity.world.remove_player(self); + self.living_entity.entity.world.remove_player(self); } pub const fn entity_id(&self) -> EntityId { - self.entity.entity_id + self.living_entity.entity.entity_id } /// Updates the current abilities the Player has @@ -174,7 +178,7 @@ impl Player { .store(0, std::sync::atomic::Ordering::Relaxed); } let teleport_id = i + 1; - let entity = &self.entity; + let entity = &self.living_entity.entity; entity.set_pos(x, y, z); entity.set_rotation(yaw, pitch); *self.awaiting_teleport.lock() = Some((teleport_id.into(), Vector3::new(x, y, z))); @@ -200,8 +204,8 @@ impl Player { pub fn can_interact_with_block_at(&self, pos: &WorldPosition, additional_range: f64) -> bool { let d = self.block_interaction_range() + additional_range; let box_pos = BoundingBox::from_block(pos); - let entity_pos = self.entity.pos.load(); - let standing_eye_height = self.entity.standing_eye_height; + let entity_pos = self.living_entity.entity.pos.load(); + let standing_eye_height = self.living_entity.entity.standing_eye_height; box_pos.squared_magnitude(Vector3 { x: entity_pos.x, y: entity_pos.y + standing_eye_height as f64, @@ -228,8 +232,8 @@ impl Player { self.client.close() } - pub fn update_health(&self, health: f32, food: i32, food_saturation: f32) { - self.entity.health.store(health); + pub fn set_health(&self, health: f32, food: i32, food_saturation: f32) { + self.living_entity.set_health(health); self.food.store(food, std::sync::atomic::Ordering::Relaxed); self.food_saturation.store(food_saturation); self.client @@ -246,7 +250,8 @@ impl Player { self.gamemode.store(gamemode); // So a little story time. I actually made an abilties_from_gamemode function. I looked at vanilla and they always send the abilties from the gamemode. But the funny thing actually is. That the client // does actually use the same method and set the abilties when receiving the CGameEvent gamemode packet. Just Mojang nonsense - self.entity + self.living_entity + .entity .world .broadcast_packet_all(&CPlayerInfoUpdate::new( 0x04, diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 97672a0ca..58ce26082 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -186,7 +186,7 @@ impl World { // spawn players for our client let token = player.client.token; for (_, existing_player) in self.current_players.lock().iter().filter(|c| c.0 != &token) { - let entity = &existing_player.entity; + let entity = &existing_player.living_entity.entity; let pos = entity.pos.load(); let gameprofile = &existing_player.gameprofile; player.client.send_packet(&CSpawnEntity::new( @@ -293,7 +293,7 @@ impl World { &[player.client.token], &CRemovePlayerInfo::new(1.into(), &[uuid]), ); - self.remove_entity(&player.entity); + self.remove_entity(&player.living_entity.entity); } pub fn remove_entity(&self, entity: &Entity) { diff --git a/pumpkin/src/world/player_chunker.rs b/pumpkin/src/world/player_chunker.rs index d6fb893aa..2c6f1c7d9 100644 --- a/pumpkin/src/world/player_chunker.rs +++ b/pumpkin/src/world/player_chunker.rs @@ -20,10 +20,10 @@ fn get_view_distance(player: &Player) -> i8 { } pub async fn player_join(world: &World, player: Arc) { - let new_watched = chunk_section_from_pos(&player.entity.block_pos.load()); + let new_watched = chunk_section_from_pos(&player.living_entity.entity.block_pos.load()); player.watched_section.store(new_watched); let watched_section = new_watched; - let chunk_pos = player.entity.chunk_pos.load(); + let chunk_pos = player.living_entity.entity.chunk_pos.load(); player.client.send_packet(&CCenterChunk { chunk_x: chunk_pos.x.into(), chunk_z: chunk_pos.z.into(),