mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Add Living Entity struct
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
46
pumpkin/src/entity/living.rs
Normal file
46
pumpkin/src/entity/living.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user