From f9bf750b65b75b9e40630f1bbb448bfe66c2733b Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Thu, 12 Sep 2024 15:42:26 +0200 Subject: [PATCH] Add Clientbound Entity Status --- .../src/client/play/c_entity_status.rs | 18 ++++++++++++ .../src/client/play/c_game_event.rs | 26 +++++++++++++++-- pumpkin-protocol/src/client/play/mod.rs | 2 ++ pumpkin/src/client/player_packet.rs | 2 +- pumpkin/src/commands/arg_player.rs | 4 +++ pumpkin/src/commands/cmd_gamemode.rs | 4 +-- pumpkin/src/entity/mod.rs | 17 +++++++++-- pumpkin/src/entity/player.rs | 8 ++++-- pumpkin/src/main.rs | 3 -- pumpkin/src/server/mod.rs | 10 +++++++ pumpkin/src/world/mod.rs | 28 ++++++++++++------- 11 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 pumpkin-protocol/src/client/play/c_entity_status.rs diff --git a/pumpkin-protocol/src/client/play/c_entity_status.rs b/pumpkin-protocol/src/client/play/c_entity_status.rs new file mode 100644 index 000000000..301fa0ee9 --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_entity_status.rs @@ -0,0 +1,18 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +#[derive(Serialize)] +#[packet(0x1F)] +pub struct CEntityStatus { + entity_id: i32, + entity_status: i8, +} + +impl CEntityStatus { + pub fn new(entity_id: i32, entity_status: i8) -> Self { + Self { + entity_id, + entity_status, + } + } +} diff --git a/pumpkin-protocol/src/client/play/c_game_event.rs b/pumpkin-protocol/src/client/play/c_game_event.rs index 8873ab210..f8a01b5cf 100644 --- a/pumpkin-protocol/src/client/play/c_game_event.rs +++ b/pumpkin-protocol/src/client/play/c_game_event.rs @@ -8,8 +8,30 @@ pub struct CGameEvent { value: f32, } +/// Somewhere you need to implement all the random stuff right? impl CGameEvent { - pub fn new(event: u8, value: f32) -> Self { - Self { event, value } + pub fn new(event: GameEvent, value: f32) -> Self { + Self { + event: event as u8, + value, + } } } + +#[repr(u8)] +pub enum GameEvent { + NoRespawnBlockAvailable, + BeginRaining, + EndRaining, + ChangeGameMode, + WinGame, + DemoEvent, + ArrowHitPlayer, + RainLevelChange, + ThunderLevelChange, + PlayPufferfishStringSound, + PlayElderGuardianMobAppearance, + EnabledRespawnScreen, + LimitedCrafting, + StartWaitingChunks, +} diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index 90a405280..493ae921c 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -9,6 +9,7 @@ mod c_close_container; mod c_disguised_chat_message; mod c_entity_animation; mod c_entity_metadata; +mod c_entity_status; mod c_entity_velocity; mod c_game_event; mod c_head_rot; @@ -51,6 +52,7 @@ pub use c_close_container::*; pub use c_disguised_chat_message::*; pub use c_entity_animation::*; pub use c_entity_metadata::*; +pub use c_entity_status::*; pub use c_entity_velocity::*; pub use c_game_event::*; pub use c_head_rot::*; diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 0cb74053c..f61457467 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -379,7 +379,7 @@ impl Player { let config = &ADVANCED_CONFIG.pvp; if config.enabled { let world = &entity.world; - let attacked_player = world.get_by_entityid(self, entity_id.0 as EntityId); + let attacked_player = world.get_player_by_entityid(entity_id.0 as EntityId); if let Some(player) = attacked_player { let victem_entity = &player.entity; if config.protect_creative diff --git a/pumpkin/src/commands/arg_player.rs b/pumpkin/src/commands/arg_player.rs index 98a71fde7..11e1fe3b6 100644 --- a/pumpkin/src/commands/arg_player.rs +++ b/pumpkin/src/commands/arg_player.rs @@ -1,8 +1,11 @@ +use std::sync::Arc; + use crate::commands::dispatcher::InvalidTreeError; use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError; use crate::commands::tree::{ConsumedArgs, RawArgs}; use crate::commands::CommandSender; use crate::commands::CommandSender::Player; +use crate::server::Server; /// todo: implement (so far only own name + @s/@p is implemented) pub fn consume_arg_player(src: &CommandSender, args: &mut RawArgs) -> Option { @@ -29,6 +32,7 @@ pub fn consume_arg_player(src: &CommandSender, args: &mut RawArgs) -> Option( src: &'a mut CommandSender, + _server: &Arc, arg_name: &str, consumed_args: &ConsumedArgs, ) -> Result<&'a crate::entity::player::Player, InvalidTreeError> { diff --git a/pumpkin/src/commands/cmd_gamemode.rs b/pumpkin/src/commands/cmd_gamemode.rs index ce48f8d91..c950df438 100644 --- a/pumpkin/src/commands/cmd_gamemode.rs +++ b/pumpkin/src/commands/cmd_gamemode.rs @@ -85,9 +85,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { }), ) .with_child(argument(ARG_TARGET, consume_arg_player).execute( - &|sender, _, args| { + &|sender, server, args| { let gamemode = parse_arg_gamemode(args)?; - let target = parse_arg_player(sender, ARG_TARGET, args)?; + let target = parse_arg_player(sender, server, ARG_TARGET, args)?; if target.gamemode.load() == gamemode { target.send_system_message(TextComponent::text(&format!( diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index d1a62bb6e..d80b2244d 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -8,7 +8,7 @@ use pumpkin_core::math::{ }; use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, EntityId}; use pumpkin_protocol::{ - client::play::{CSetEntityMetadata, Metadata}, + client::play::{CEntityStatus, CSetEntityMetadata, Metadata}, VarInt, }; @@ -121,8 +121,21 @@ impl Entity { self.pitch.store(pitch); } + /// Kills the Entity + /// + /// This is simliar 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 async fn remove(&mut self) { + pub fn remove(&self) { self.world.remove_entity(self); } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 175b8d8b5..252d4dcc4 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -18,7 +18,7 @@ use pumpkin_protocol::{ bytebuf::{packet_id::Packet, DeserializerError}, client::play::{ CGameEvent, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CSyncPlayerPosition, - CSystemChatMessage, PlayerAction, + CSystemChatMessage, GameEvent, PlayerAction, }, server::play::{ SChatCommand, SChatMessage, SClickContainer, SClientInformationPlay, SConfirmTeleport, @@ -248,8 +248,10 @@ impl Player { actions: vec![PlayerAction::UpdateGameMode((gamemode as i32).into())], }], )); - self.client - .send_packet(&CGameEvent::new(3, gamemode.to_f32().unwrap())); + self.client.send_packet(&CGameEvent::new( + GameEvent::ChangeGameMode, + gamemode.to_f32().unwrap(), + )); } pub fn send_system_message(&self, text: TextComponent) { diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index a4cb4f174..5f27a7607 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -170,10 +170,7 @@ fn main() -> io::Result<()> { if closed { if let Some(player) = players.remove(&token) { player.remove().await; - dbg!("b"); let connection = &mut player.client.connection.lock(); - dbg!("c"); - poll.registry().deregister(connection.by_ref())?; } } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 292e3345a..86c18851b 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -158,6 +158,16 @@ impl Server { } } + /// Searches every world for a player by name + pub fn get_player_by_name(&self, name: &str) -> Option> { + for world in self.worlds.iter() { + if let Some(player) = world.get_player_by_name(name) { + return Some(player); + } + } + None + } + /// Generates a new entity id /// This should be global pub fn new_entity_id(&self) -> EntityId { diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index b65ad3720..18a21bec5 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -11,7 +11,7 @@ use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_protocol::{ client::play::{ CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, CRemoveEntities, - CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata, PlayerAction, + CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, GameEvent, Metadata, PlayerAction, }, uuid::UUID, ClientPacket, VarInt, @@ -216,8 +216,10 @@ impl World { self.broadcast_packet_all(&packet) } - // Start waiting for level chunks - player.client.send_packet(&CGameEvent::new(13, 0.0)); + // Start waiting for level chunks, Sets the "Loading Terrain" screen + player + .client + .send_packet(&CGameEvent::new(GameEvent::StartWaitingChunks, 0.0)); // Spawn in inital chunks player_chunker::player_join(self, player.clone()).await; @@ -258,13 +260,9 @@ impl World { dbg!("DONE CHUNKS", inst.elapsed()); } - pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option> { - for (_, player) in self - .current_players - .lock() - .iter() - .filter(|c| c.0 != &from.client.token) - { + /// Gets a Player by entity id + pub fn get_player_by_entityid(&self, id: EntityId) -> Option> { + for (_, player) in self.current_players.lock().iter() { if player.entity_id() == id { return Some(player.clone()); } @@ -272,6 +270,16 @@ impl World { None } + /// Gets a Player by name + pub fn get_player_by_name(&self, name: &str) -> Option> { + for (_, player) in self.current_players.lock().iter() { + if player.gameprofile.name == name { + return Some(player.clone()); + } + } + None + } + pub fn add_player(&self, token: Token, player: Arc) { self.current_players.lock().insert(token, player); }