mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Living Entity: Proper death
Now we wait until despawning the entity so the death animation is played
This commit is contained in:
@@ -51,7 +51,7 @@ rayon = "1.10"
|
||||
parking_lot = { version = "0.12", features = ["send_guard"] }
|
||||
crossbeam = "0.8"
|
||||
|
||||
uuid = { version = "1.14", features = ["serde", "v3", "v4"] }
|
||||
uuid = { version = "1.15", features = ["serde", "v3", "v4"] }
|
||||
derive_more = { version = "2.0", features = ["full"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
60
assets/entity_statuses.json
Normal file
60
assets/entity_statuses.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"ADD_SPRINTING_PARTICLES_OR_RESET_SPAWNER_MINECART_SPAWN_DELAY": 1,
|
||||
"PLAY_DEATH_SOUND_OR_ADD_PROJECTILE_HIT_PARTICLES": 3,
|
||||
"PLAY_ATTACK_SOUND": 4,
|
||||
"ADD_NEGATIVE_PLAYER_REACTION_PARTICLES": 6,
|
||||
"ADD_POSITIVE_PLAYER_REACTION_PARTICLES": 7,
|
||||
"SHAKE_OFF_WATER": 8,
|
||||
"CONSUME_ITEM": 9,
|
||||
"SET_SHEEP_EAT_GRASS_TIMER_OR_PRIME_TNT_MINECART": 10,
|
||||
"LOOK_AT_VILLAGER": 11,
|
||||
"ADD_VILLAGER_HEART_PARTICLES": 12,
|
||||
"ADD_VILLAGER_ANGRY_PARTICLES": 13,
|
||||
"ADD_VILLAGER_HAPPY_PARTICLES": 14,
|
||||
"ADD_WITCH_PARTICLES": 15,
|
||||
"PLAY_CURE_ZOMBIE_VILLAGER_SOUND": 16,
|
||||
"EXPLODE_FIREWORK_CLIENT": 17,
|
||||
"ADD_BREEDING_PARTICLES": 18,
|
||||
"RESET_SQUID_THRUST_TIMER": 19,
|
||||
"PLAY_SPAWN_EFFECTS": 20,
|
||||
"PLAY_GUARDIAN_ATTACK_SOUND": 21,
|
||||
"USE_REDUCED_DEBUG_INFO": 22,
|
||||
"USE_FULL_DEBUG_INFO": 23,
|
||||
"SET_OP_LEVEL_0": 24,
|
||||
"SET_OP_LEVEL_1": 25,
|
||||
"SET_OP_LEVEL_2": 26,
|
||||
"SET_OP_LEVEL_3": 27,
|
||||
"SET_OP_LEVEL_4": 28,
|
||||
"BLOCK_WITH_SHIELD": 29,
|
||||
"BREAK_SHIELD": 30,
|
||||
"PULL_HOOKED_ENTITY": 31,
|
||||
"HIT_ARMOR_STAND": 32,
|
||||
"STOP_LOOKING_AT_VILLAGER": 34,
|
||||
"USE_TOTEM_OF_UNDYING": 35,
|
||||
"ADD_DOLPHIN_HAPPY_VILLAGER_PARTICLES": 38,
|
||||
"STUN_RAVAGER": 39,
|
||||
"TAME_OCELOT_FAILED": 40,
|
||||
"TAME_OCELOT_SUCCESS": 41,
|
||||
"ADD_SPLASH_PARTICLES": 42,
|
||||
"CREATE_EATING_PARTICLES": 45,
|
||||
"ADD_PORTAL_PARTICLES": 46,
|
||||
"BREAK_MAINHAND": 47,
|
||||
"BREAK_OFFHAND": 48,
|
||||
"BREAK_HEAD": 49,
|
||||
"BREAK_CHEST": 50,
|
||||
"BREAK_LEGS": 51,
|
||||
"BREAK_FEET": 52,
|
||||
"DRIP_HONEY": 53,
|
||||
"DRIP_RICH_HONEY": 54,
|
||||
"SWAP_HANDS": 55,
|
||||
"RESET_WOLF_SHAKE": 56,
|
||||
"PREPARE_RAM": 58,
|
||||
"FINISH_RAM": 59,
|
||||
"ADD_DEATH_PARTICLES": 60,
|
||||
"EARS_TWITCH": 61,
|
||||
"SONIC_BOOM": 62,
|
||||
"START_DIGGING": 63,
|
||||
"PEEKING": 64,
|
||||
"BREAK_BODY": 65,
|
||||
"INVULNERABLE_CREAKING_HIT": 66
|
||||
}
|
||||
@@ -40,7 +40,6 @@
|
||||
"SKELETON_CONVERTS_TO_STRAY": 1048,
|
||||
"CRAFTER_CRAFTS": 1049,
|
||||
"CRAFTER_FAILS": 1050,
|
||||
"field_51787": 1051,
|
||||
"COMPOSTER_USED": 1500,
|
||||
"LAVA_EXTINGUISHED": 1501,
|
||||
"REDSTONE_TORCH_BURNS_OUT": 1502,
|
||||
|
||||
@@ -8,6 +8,7 @@ mod biome;
|
||||
mod chunk_status;
|
||||
mod damage_type;
|
||||
mod entity_pose;
|
||||
mod entity_status;
|
||||
mod entity_type;
|
||||
mod fluid;
|
||||
mod game_event;
|
||||
@@ -44,6 +45,7 @@ pub fn main() {
|
||||
write_generated_file(item::build(), "item.rs");
|
||||
write_generated_file(fluid::build(), "fluid.rs");
|
||||
write_generated_file(status_effect::build(), "status_effect.rs");
|
||||
write_generated_file(entity_status::build(), "entity_status.rs");
|
||||
}
|
||||
|
||||
pub fn array_to_tokenstream(array: &[String]) -> TokenStream {
|
||||
|
||||
27
pumpkin-data/build/entity_status.rs
Normal file
27
pumpkin-data/build/entity_status.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use heck::ToPascalCase;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
pub(crate) fn build() -> TokenStream {
|
||||
println!("cargo:rerun-if-changed=../assets/entity_statuses.json");
|
||||
|
||||
let events: HashMap<String, u8> =
|
||||
serde_json::from_str(include_str!("../../assets/entity_statuses.json"))
|
||||
.expect("Failed to parse entity_statuses.json");
|
||||
let mut variants = TokenStream::new();
|
||||
|
||||
for (event, id) in events.iter() {
|
||||
let name = format_ident!("{}", event.to_pascal_case());
|
||||
variants.extend([quote! {
|
||||
#name = #id,
|
||||
}]);
|
||||
}
|
||||
quote! {
|
||||
#[repr(u8)]
|
||||
pub enum EntityStatus {
|
||||
#variants
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ pub mod game_event {
|
||||
}
|
||||
|
||||
pub mod entity {
|
||||
include!(concat!(env!("OUT_DIR"), "/entity_status.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/status_effect.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/spawn_egg.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/entity_type.rs"));
|
||||
|
||||
@@ -28,7 +28,7 @@ dashmap = "6.1"
|
||||
num-traits = "0.2"
|
||||
|
||||
# Compression
|
||||
flate2 = "1.0"
|
||||
flate2 = "1.1"
|
||||
lz4 = "1.28"
|
||||
zstd = "0.13.3"
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
use std::sync::atomic::AtomicU8;
|
||||
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::entity::{EffectType, EntityStatus};
|
||||
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,
|
||||
},
|
||||
client::play::{CDamageEvent, CSetEquipment, EquipmentSlot, MetaDataType, Metadata},
|
||||
codec::slot::Slot,
|
||||
};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
@@ -36,6 +35,7 @@ pub struct LivingEntity {
|
||||
pub last_damage_taken: AtomicCell<f32>,
|
||||
/// The current health level of the entity.
|
||||
pub health: AtomicCell<f32>,
|
||||
pub death_time: AtomicU8,
|
||||
/// The distance the entity has been falling
|
||||
pub fall_distance: AtomicCell<f32>,
|
||||
pub active_effects: Mutex<HashMap<EffectType, Effect>>,
|
||||
@@ -49,6 +49,7 @@ impl LivingEntity {
|
||||
last_damage_taken: AtomicCell::new(0.0),
|
||||
health: AtomicCell::new(20.0),
|
||||
fall_distance: AtomicCell::new(0.0),
|
||||
death_time: AtomicU8::new(0),
|
||||
active_effects: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
@@ -211,22 +212,16 @@ impl LivingEntity {
|
||||
pub async fn kill(&self) {
|
||||
self.set_health(0.0).await;
|
||||
|
||||
// Spawns death smoke particles
|
||||
// Plays the death sound
|
||||
self.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CEntityStatus::new(self.entity.entity_id, 60))
|
||||
.send_entity_status(
|
||||
&self.entity,
|
||||
EntityStatus::PlayDeathSoundOrAddProjectileHitParticles,
|
||||
)
|
||||
.await;
|
||||
// Plays the death sound and death animation
|
||||
self.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.broadcast_packet_all(&CEntityStatus::new(self.entity.entity_id, 3))
|
||||
.await;
|
||||
// TODO: wait
|
||||
self.entity.remove().await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,6 +236,21 @@ impl EntityBase for LivingEntity {
|
||||
self.time_until_regen
|
||||
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
if self.health.load() <= 0.0 {
|
||||
let time = self
|
||||
.death_time
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
if time >= 20 {
|
||||
// Spawn Death particles
|
||||
self.entity
|
||||
.world
|
||||
.read()
|
||||
.await
|
||||
.send_entity_status(&self.entity, EntityStatus::AddDeathParticles)
|
||||
.await;
|
||||
self.entity.remove().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn damage(&self, amount: f32, damage_type: DamageType) -> bool {
|
||||
let world = self.entity.world.read().await;
|
||||
|
||||
@@ -13,7 +13,7 @@ use crossbeam::atomic::AtomicCell;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
use pumpkin_data::{
|
||||
damage::DamageType,
|
||||
entity::{EffectType, EntityType},
|
||||
entity::{EffectType, EntityStatus, EntityType},
|
||||
item::Operation,
|
||||
particle::Particle,
|
||||
sound::{Sound, SoundCategory},
|
||||
@@ -24,8 +24,8 @@ use pumpkin_protocol::{
|
||||
RawPacket, ServerPacket,
|
||||
bytebuf::packet::Packet,
|
||||
client::play::{
|
||||
CAcknowledgeBlockChange, CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus,
|
||||
CGameEvent, CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate,
|
||||
CAcknowledgeBlockChange, CActionBar, CCombatDeath, CDisguisedChatMessage, CGameEvent,
|
||||
CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate,
|
||||
CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle, CSystemChatMessage,
|
||||
CTitleText, CUnloadChunk, CUpdateMobEffect, GameEvent, MetaDataType, PlayerAction,
|
||||
},
|
||||
@@ -613,11 +613,16 @@ impl Player {
|
||||
|
||||
/// syncs the players permission level with the client
|
||||
pub async fn send_permission_lvl_update(&self) {
|
||||
self.client
|
||||
.send_packet(&CEntityStatus::new(
|
||||
self.entity_id(),
|
||||
24 + self.permission_lvl.load() as i8,
|
||||
))
|
||||
let status = match self.permission_lvl.load() {
|
||||
PermissionLvl::Zero => EntityStatus::SetOpLevel0,
|
||||
PermissionLvl::One => EntityStatus::SetOpLevel1,
|
||||
PermissionLvl::Two => EntityStatus::SetOpLevel2,
|
||||
PermissionLvl::Three => EntityStatus::SetOpLevel3,
|
||||
PermissionLvl::Four => EntityStatus::SetOpLevel4,
|
||||
};
|
||||
self.world()
|
||||
.await
|
||||
.send_entity_status(&self.living_entity.entity, status)
|
||||
.await;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ use border::Worldborder;
|
||||
use explosion::Explosion;
|
||||
use pumpkin_config::BasicConfiguration;
|
||||
use pumpkin_data::{
|
||||
entity::EntityType,
|
||||
entity::{EntityStatus, EntityType},
|
||||
particle::Particle,
|
||||
sound::{Sound, SoundCategory},
|
||||
world::WorldEvent,
|
||||
@@ -32,8 +32,8 @@ use pumpkin_macros::send_cancellable;
|
||||
use pumpkin_protocol::{
|
||||
ClientPacket,
|
||||
client::play::{
|
||||
CChunkData, CGameEvent, CLogin, CPlayerInfoUpdate, CRemoveEntities, CRemovePlayerInfo,
|
||||
CSpawnEntity, GameEvent, PlayerAction,
|
||||
CChunkData, CEntityStatus, CGameEvent, CLogin, CPlayerInfoUpdate, CRemoveEntities,
|
||||
CRemovePlayerInfo, CSpawnEntity, GameEvent, PlayerAction,
|
||||
},
|
||||
};
|
||||
use pumpkin_protocol::{client::play::CLevelEvent, codec::identifier::Identifier};
|
||||
@@ -149,6 +149,12 @@ impl World {
|
||||
self.level.save().await;
|
||||
}
|
||||
|
||||
pub async fn send_entity_status(&self, entity: &Entity, status: EntityStatus) {
|
||||
// TODO: only nearby
|
||||
self.broadcast_packet_all(&CEntityStatus::new(entity.entity_id, status as i8))
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Broadcasts a packet to all connected players within the world.
|
||||
///
|
||||
/// Sends the specified packet to every player currently logged in to the world.
|
||||
|
||||
Reference in New Issue
Block a user