diff --git a/pumpkin/src/server/connection_cache.rs b/pumpkin/src/server/connection_cache.rs index 93b78278d..781dbf220 100644 --- a/pumpkin/src/server/connection_cache.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -4,7 +4,7 @@ use core::error; use pumpkin_config::BasicConfiguration; use pumpkin_data::packet::CURRENT_MC_PROTOCOL; use pumpkin_protocol::{ - Players, StatusResponse, Version, + Players, Sample, StatusResponse, Version, codec::var_int::VarInt, java::client::{config::CPluginMessage, status::CStatusResponse}, }; @@ -13,8 +13,10 @@ use std::{ fs::{self}, path::Path, }; +use uuid::Uuid; const DEFAULT_ICON: &[u8] = include_bytes!("../../../assets/default_icon.png"); +const MAX_SAMPLE_PLAYERS: usize = 12; fn load_icon_from_file>(path: P) -> Result> { let buf = fs::read(path)?; @@ -41,6 +43,7 @@ pub struct CachedStatus { // We cache the json response here so we don't parse it every time someone makes a status request. // Keep in mind that we must parse this again when the StatusResponse changes, which usually happen when a player joins or leaves. status_response_json: String, + player_samples: Vec<(Uuid, String)>, } pub struct CachedBranding { @@ -79,6 +82,7 @@ impl CachedStatus { Self { status_response, status_response_json, + player_samples: Vec::new(), } } @@ -86,37 +90,48 @@ impl CachedStatus { CStatusResponse::new(self.status_response_json.clone()) } - // TODO: Player samples - pub fn add_player(&mut self, _player: &Player) { + pub fn add_player(&mut self, player: &Player) { let status_response = &mut self.status_response; if let Some(players) = &mut status_response.players { - // TODO - // if player - // .client - // .added_to_server_listing - // .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - // .is_ok() - // { players.online = players.online.saturating_add(1); - // } + + let player_id = player.gameprofile.id; + let player_name = player.gameprofile.name.clone(); + + if !self.player_samples.iter().any(|(id, _)| *id == player_id) { + self.player_samples.push((player_id, player_name.clone())); + + players.sample = self.player_samples + .iter() + .take(MAX_SAMPLE_PLAYERS) + .map(|(id, name)| Sample { + name: name.clone(), + id: id.to_string(), + }) + .collect(); + } } self.status_response_json = serde_json::to_string(&status_response) .expect("Failed to parse status response into JSON"); } - pub fn remove_player(&mut self, _player: &Player) { + pub fn remove_player(&mut self, player: &Player) { let status_response = &mut self.status_response; if let Some(players) = &mut status_response.players { - // TODO - // if player - // .client - // .added_to_server_listing - // .compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) - // .is_ok() - // { players.online = players.online.saturating_sub(1); - // } + + let player_id = player.gameprofile.id; + self.player_samples.retain(|(id, _)| *id != player_id); + + players.sample = self.player_samples + .iter() + .take(MAX_SAMPLE_PLAYERS) + .map(|(id, name)| Sample { + name: name.clone(), + id: id.to_string(), + }) + .collect(); } self.status_response_json = serde_json::to_string(&status_response)