Add Living Entity struct

This commit is contained in:
Snowiiii
2024-10-11 12:54:42 +02:00
parent 4076cda942
commit 9ac4bdbd50
8 changed files with 89 additions and 47 deletions

View File

@@ -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

View File

@@ -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<Server>, 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<Server>, 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,

View File

@@ -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),

View File

@@ -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<f32>,
}
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();
}
}

View File

@@ -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<World>,
/// The entity's current health level.
pub health: AtomicCell<f32>,
/// The entity's current position in the world
pub pos: AtomicCell<Vector3<f64>>,
@@ -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);

View File

@@ -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,

View File

@@ -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) {

View File

@@ -20,10 +20,10 @@ fn get_view_distance(player: &Player) -> i8 {
}
pub async fn player_join(world: &World, player: Arc<Player>) {
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(),