From 15d9920826332f251761c8340952fcbba206969a Mon Sep 17 00:00:00 2001 From: lokka30 <59464084+lokka30@users.noreply.github.com> Date: Sun, 27 Oct 2024 23:18:28 +0800 Subject: [PATCH] Implement max player limit (#194) * Implement max player limit * Removed unnecessary `return`. * Moved server full check to start of `handle_login_start` Also removed unnecessary debug log. * Use instead of for max players; add disable logic * Fix semicolon missing --- pumpkin-config/src/lib.rs | 2 +- pumpkin/src/client/client_packet.rs | 18 +++++++++++++++--- pumpkin/src/server/mod.rs | 9 +++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index b2fd20d05..3295b186f 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -64,7 +64,7 @@ pub struct BasicConfiguration { /// The seed for world generation. #[serde(default = "String::new")] pub seed: String, - /// The maximum number of players allowed on the server. + /// The maximum number of players allowed on the server. Specifying `0` disables the limit. #[serde_inline_default(10000)] pub max_players: u32, /// The maximum view distance for players. diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 92199817f..8fd1ac97a 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -1,4 +1,4 @@ -use num_traits::FromPrimitive; +use num_traits::{FromPrimitive, ToPrimitive}; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_core::text::TextComponent; use pumpkin_protocol::{ @@ -78,6 +78,18 @@ impl Client { pub async fn handle_login_start(&self, server: &Server, login_start: SLoginStart) { log::debug!("login start"); + // Don't allow new logons when server is full. + // If max players is set to zero, then there is no max player count enforced. + // TODO: If client is an operator or otherwise suitable elevated permissions, allow client to bypass this requirement. + let max_players = BASIC_CONFIG + .max_players + .to_usize() + .expect("Unable to convert to usize"); + if max_players > 0 && server.get_player_count().await >= max_players { + self.kick("The server is currently full, please try again later") + .await; + } + if !Self::is_valid_player_name(&login_start.name) { self.kick("Invalid characters in username").await; return; @@ -166,14 +178,14 @@ impl Client { // Don't allow duplicate UUIDs if let Some(online_player) = &server.get_player_by_uuid(profile.id).await { - log::debug!("Player (IP '{}', username '{}') tried to log in with the same UUID ('{}') as an online player (IP '{}', username '{}')", &self.address.lock().await.to_string(), &profile.name, &profile.id.to_string(), &online_player.client.address.lock().await.to_string(), &online_player.gameprofile.name); + log::debug!("Player (IP '{}', username '{}') tried to log in with the same UUID ('{}') as an online player (IP '{}', username '{}')", &self.address.lock().await, &profile.name, &profile.id, &online_player.client.address.lock().await, &online_player.gameprofile.name); self.kick("You are already connected to this server").await; return; } // Don't allow a duplicate username if let Some(online_player) = &server.get_player_by_name(&profile.name).await { - log::debug!("A player (IP '{}', attempted username '{}') tried to log in with the same username as an online player (UUID '{}', IP '{}', username '{}')", &self.address.lock().await.to_string(), &profile.name, &profile.id.to_string(), &online_player.client.address.lock().await.to_string(), &online_player.gameprofile.name); + log::debug!("A player (IP '{}', attempted username '{}') tried to log in with the same username as an online player (UUID '{}', IP '{}', username '{}')", &self.address.lock().await, &profile.name, &profile.id, &online_player.client.address.lock().await, &online_player.gameprofile.name); self.kick("A player with this username is already connected") .await; return; diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index b8ab5643b..60264c99b 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -164,6 +164,15 @@ impl Server { None } + /// Get the player count sum in all worlds + pub async fn get_player_count(&self) -> usize { + let mut count = 0; + for world in &self.worlds { + count += world.current_players.lock().await.len(); + } + count + } + /// Generates a new entity id /// This should be global pub fn new_entity_id(&self) -> EntityId {