From 1a1a7e351c4b43096059b3f83b32cbd2b4d72cbc Mon Sep 17 00:00:00 2001 From: lokka30 <59464084+lokka30@users.noreply.github.com> Date: Sun, 27 Oct 2024 21:50:52 +0800 Subject: [PATCH] Add player join and quit messages. (#192) * pumpkin: Add player join and quit messages. This implementation will send a 'joined the game' and 'left the game' message akin to the vanilla server when a player joins or disconnects respectively. One caveat with this implementation is that it only broadcasts the join/quit messages to players in the same world, unlike the vanilla server, which broadcasts it to all online players (regardless of their world). I discussed this with Alex who suggested to keep it as per-world (seemingly for now?) - [Discord conversation](https://discord.com/channels/1268592337445978193/1268611318617866261/1299668206734147597). Regardless, this implementation works perfectly on my end. (yes, it has been tested) * pumpkin: added logs for join/quit * Move disconnect message code to world from player Compiles and runs, sends join and quit messages in console with no errors. * Move join message from server to world --- pumpkin/src/entity/player.rs | 17 ++++++----------- pumpkin/src/server/mod.rs | 1 + pumpkin/src/world/mod.rs | 23 ++++++++++++++++++++++- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 4f6cd3902..028871a61 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -154,7 +154,9 @@ impl Player { /// Removes the Player out of the current World #[allow(unused_variables)] pub async fn remove(&self) { - self.living_entity.entity.world.remove_player(self).await; + let world = &self.living_entity.entity.world; + + world.remove_player(self).await; let watched = self.watched_section.load(); let view_distance = i32::from(get_view_distance(self).await); @@ -254,20 +256,13 @@ impl Player { log::debug!("Done waiting for chunk batches"); // Decrement value of watched chunks - let chunks_to_clean = self - .living_entity - .entity - .world - .mark_chunks_as_not_watched(&watched_chunks); + let chunks_to_clean = world.mark_chunks_as_not_watched(&watched_chunks); // Remove chunks with no watchers from the cache - self.living_entity - .entity - .world - .clean_chunks(&chunks_to_clean); + world.clean_chunks(&chunks_to_clean); // Remove left over entries from all possiblily loaded chunks - self.living_entity.entity.world.clean_memory(&radial_chunks); + world.clean_memory(&radial_chunks); log::debug!( "Removed player id {} ({}) ({} chunks remain cached)", diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 4e5feb505..b8ab5643b 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -113,6 +113,7 @@ impl Server { self.server_listing.lock().await.add_player(); } } + (player, world.clone()) } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index e43ea4054..7add9beae 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -13,6 +13,7 @@ use num_traits::ToPrimitive; use pumpkin_config::BasicConfiguration; use pumpkin_core::math::vector2::Vector2; use pumpkin_core::math::{position::WorldPosition, vector3::Vector3}; +use pumpkin_core::text::{color::NamedColor, TextComponent}; use pumpkin_entity::{entity_type::EntityType, EntityId}; use pumpkin_protocol::{ client::play::{CBlockUpdate, CSoundEffect, CWorldEvent}, @@ -482,7 +483,18 @@ impl World { } pub async fn add_player(&self, uuid: uuid::Uuid, player: Arc) { - self.current_players.lock().await.insert(uuid, player); + self.current_players + .lock() + .await + .insert(uuid, player.clone()); + + // Handle join message + let msg_txt = format!("{} joined the game.", player.gameprofile.name.as_str()); + let msg_comp = TextComponent::text(msg_txt.as_str()).color_named(NamedColor::Yellow); + for player in self.current_players.lock().await.values() { + player.send_system_message(&msg_comp).await; + } + log::info!("{}", msg_comp.to_pretty_console()); } pub async fn remove_player(&self, player: &Player) { @@ -498,6 +510,15 @@ impl World { ) .await; self.remove_entity(&player.living_entity.entity).await; + + // Send disconnect message / quit message to players in the same world + let disconn_msg_txt = format!("{} left the game.", player.gameprofile.name.as_str()); + let disconn_msg_cmp = + TextComponent::text(disconn_msg_txt.as_str()).color_named(NamedColor::Yellow); + for player in self.current_players.lock().await.values() { + player.send_system_message(&disconn_msg_cmp).await; + } + log::info!("{}", disconn_msg_cmp.to_pretty_console()); } pub async fn remove_entity(&self, entity: &Entity) {