use connection_cache::{CachedBranding, CachedStatus}; use key_store::KeyStore; use pumpkin_config::BASIC_CONFIG; use pumpkin_core::GameMode; use pumpkin_entity::EntityId; use pumpkin_inventory::drag_handler::DragHandler; use pumpkin_inventory::{Container, OpenContainer}; use pumpkin_protocol::client::login::CEncryptionRequest; use pumpkin_protocol::{client::config::CPluginMessage, ClientPacket}; use pumpkin_registry::Registry; use pumpkin_world::dimension::Dimension; use std::collections::HashMap; use std::{ sync::{ atomic::{AtomicI32, Ordering}, Arc, }, time::Duration, }; use tokio::sync::Mutex; use tokio::sync::RwLock; use crate::client::EncryptionError; use crate::{ client::Client, command::{default_dispatcher, dispatcher::CommandDispatcher}, entity::player::Player, world::World, }; mod connection_cache; mod key_store; pub mod ticker; pub const CURRENT_MC_VERSION: &str = "1.21.3"; pub struct Server { key_store: KeyStore, server_listing: Mutex, server_branding: CachedBranding, pub command_dispatcher: Arc>, pub worlds: Vec>, /// Cache the registry so we don't have to parse it every time a player joins pub cached_registry: Vec, pub open_containers: RwLock>, pub drag_handler: DragHandler, entity_id: AtomicI32, /// Used for Authentication, None is Online mode is disabled pub auth_client: Option, } impl Server { #[allow(clippy::new_without_default)] #[must_use] pub fn new() -> Self { // TODO: only create when needed let auth_client = if BASIC_CONFIG.online_mode { Some( reqwest::Client::builder() .timeout(Duration::from_millis(5000)) .build() .expect("Failed to to make reqwest client"), ) } else { None }; // First register default command, after that plugins can put in their own let command_dispatcher = default_dispatcher(); let world = World::load(Dimension::OverWorld.into_level( // TODO: load form config "./world".parse().unwrap(), )); Self { cached_registry: Registry::get_static(), open_containers: RwLock::new(HashMap::new()), drag_handler: DragHandler::new(), // 0 is invalid entity_id: 2.into(), worlds: vec![Arc::new(world)], command_dispatcher, auth_client, key_store: KeyStore::new(), server_listing: Mutex::new(CachedStatus::new()), server_branding: CachedBranding::new(), } } pub async fn add_player(&self, client: Arc) -> (Arc, Arc) { let entity_id = self.new_entity_id(); let gamemode = match BASIC_CONFIG.default_gamemode { GameMode::Undefined => GameMode::Survival, game_mode => game_mode, }; // Basically the default world // TODO: select default from config let world = &self.worlds[0]; let player = Arc::new(Player::new(client, world.clone(), entity_id, gamemode).await); world .add_player(player.gameprofile.id, player.clone()) .await; // TODO: Config if we want increase online if let Some(config) = player.client.config.lock().await.as_ref() { // TODO: Config so we can also just ignore this hehe if config.server_listing { self.server_listing.lock().await.add_player(); } } (player, world.clone()) } pub async fn remove_player(&self) { // TODO: Config if we want increase online self.server_listing.lock().await.remove_player(); } pub async fn try_get_container( &self, player_id: EntityId, container_id: u64, ) -> Option>>> { let open_containers = self.open_containers.read().await; open_containers .get(&container_id)? .try_open(player_id) .cloned() } /// Sends a Packet to all Players in all worlds pub async fn broadcast_packet_all

(&self, packet: &P) where P: ClientPacket, { for world in &self.worlds { world.broadcast_packet_all(packet).await; } } /// Searches every world for a player by username pub async fn get_player_by_name(&self, name: &str) -> Option> { for world in &self.worlds { if let Some(player) = world.get_player_by_name(name).await { return Some(player); } } None } /// Searches every world for a player by UUID pub async fn get_player_by_uuid(&self, id: uuid::Uuid) -> Option> { for world in &self.worlds { if let Some(player) = world.get_player_by_uuid(id).await { return Some(player); } } 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 { self.entity_id.fetch_add(1, Ordering::SeqCst) } pub fn get_branding(&self) -> CPluginMessage<'_> { self.server_branding.get_branding() } pub fn get_status(&self) -> &Mutex { &self.server_listing } pub fn encryption_request<'a>( &'a self, verification_token: &'a [u8; 4], should_authenticate: bool, ) -> CEncryptionRequest<'_> { self.key_store .encryption_request("", verification_token, should_authenticate) } pub fn decrypt(&self, data: &[u8]) -> Result, EncryptionError> { self.key_store.decrypt(data) } pub fn digest_secret(&self, secret: &[u8]) -> String { self.key_store.get_digest(secret) } async fn tick(&self) { for world in &self.worlds { world.tick().await; } } }