Skip to content

Commit

Permalink
Implement max player limit (#194)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
lokka30 authored Oct 27, 2024
1 parent 58ace31 commit 15d9920
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 4 deletions.
2 changes: 1 addition & 1 deletion pumpkin-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 15 additions & 3 deletions pumpkin/src/client/client_packet.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions pumpkin/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 15d9920

Please sign in to comment.