use std::{num::NonZeroU8, sync::Arc}; use crate::{ entity::player::ChatMode, net::{ClientPlatform, PlayerConfig, can_not_join, java::JavaClient}, server::Server, }; use core::str; use pumpkin_data::{registry::Registry, tag::RegistryKey}; use pumpkin_protocol::{ ConnectionState, java::{ client::config::{CFinishConfig, CRegistryData, CUpdateTags, RegistryEntry}, server::config::{ ResourcePackResponseResult, SClientInformationConfig, SConfigCookieResponse, SConfigResourcePack, SKnownPacks, SPluginMessage, }, }, }; use pumpkin_util::{Hand, text::TextComponent}; impl JavaClient { pub async fn handle_client_information_config( &self, client_information: SClientInformationConfig, ) { log::debug!("Handling client settings"); if client_information.view_distance <= 0 { self.kick(TextComponent::text( "Cannot have zero or negative view distance!", )) .await; return; } if let (Ok(main_hand), Ok(chat_mode)) = ( Hand::try_from(client_information.main_hand.0), ChatMode::try_from(client_information.chat_mode.0), ) { *self.config.lock().await = Some(PlayerConfig { locale: client_information.locale, // client_information.view_distance was checked above to be > 0 so compiler should optimize this out. view_distance: NonZeroU8::new(client_information.view_distance as u8).unwrap(), chat_mode, chat_colors: client_information.chat_colors, skin_parts: client_information.skin_parts, main_hand, text_filtering: client_information.text_filtering, server_listing: client_information.server_listing, }); } else { self.kick(TextComponent::text("Invalid hand or chat type")) .await; } } pub async fn handle_plugin_message(&self, plugin_message: SPluginMessage) { log::debug!("Handling plugin message"); if plugin_message .channel .to_string() .starts_with("minecraft:brand") { log::debug!("Got a client brand"); match str::from_utf8(&plugin_message.data) { Ok(brand) => *self.brand.lock().await = Some(brand.to_string()), Err(e) => self.kick(TextComponent::text(e.to_string())).await, } } } pub async fn handle_resource_pack_response( &self, server: &Server, packet: SConfigResourcePack, ) { let resource_config = &server.advanced_config.resource_pack; if resource_config.enabled { let expected_uuid = uuid::Uuid::new_v3(&uuid::Uuid::NAMESPACE_DNS, resource_config.url.as_bytes()); if packet.uuid == expected_uuid { match packet.response_result() { ResourcePackResponseResult::DownloadSuccess => { log::trace!( "Client {} successfully downloaded the resource pack", self.id ); } ResourcePackResponseResult::DownloadFail => { log::warn!( "Client {} failed to downloaded the resource pack. Is it available on the internet?", self.id ); } ResourcePackResponseResult::Downloaded => { log::trace!("Client {} already has the resource pack", self.id); } ResourcePackResponseResult::Accepted => { log::trace!("Client {} accepted the resource pack", self.id); // Return here to wait for the next response update return; } ResourcePackResponseResult::Declined => { log::trace!("Client {} declined the resource pack", self.id); } ResourcePackResponseResult::InvalidUrl => { log::warn!( "Client {} reported that the resource pack URL is invalid!", self.id ); } ResourcePackResponseResult::ReloadFailed => { log::trace!("Client {} failed to reload the resource pack", self.id); } ResourcePackResponseResult::Discarded => { log::trace!("Client {} discarded the resource pack", self.id); } ResourcePackResponseResult::Unknown(result) => { log::warn!( "Client {} responded with a bad result: {}!", self.id, result ); } } } else { log::warn!( "Client {} returned a response for a resource pack we did not set!", self.id ); } } else { log::warn!( "Client {} returned a response for a resource pack that was not enabled!", self.id ); } self.send_known_packs().await; } pub fn handle_config_cookie_response(&self, packet: &SConfigCookieResponse) { // TODO: allow plugins to access this log::debug!( "Received cookie_response[config]: key: \"{}\", has_payload: \"{}\", payload_length: \"{:?}\"", packet.key, packet.has_payload, packet.payload.as_ref().map(|p| p.len()), ); } pub async fn handle_known_packs(&self, _config_acknowledged: SKnownPacks) { log::debug!("Handling known packs"); let mut tags_to_send = Vec::new(); let registry = Registry::get_synced(self.version.load()); for registry in registry { let entries: Vec = registry .registry_entries .iter() .map(|r| RegistryEntry::new(r.entry_id.clone(), r.data.clone())) .collect(); self.send_packet_now(&CRegistryData::new(®istry.registry_id, &entries)) .await; if let Some(tag) = RegistryKey::from_string(®istry.registry_id.path) && pumpkin_data::tag::get_registry_key_tags(self.version.load(), tag).is_some() { tags_to_send.push(tag); } } self.send_packet_now(&CUpdateTags::new(&tags_to_send)).await; // We are done with configuring log::debug!("Finished config"); self.send_packet_now(&CFinishConfig).await; } pub async fn handle_config_acknowledged(self: &Arc, server: &Server) { log::debug!("Handling config acknowledgement"); self.connection_state.store(ConnectionState::Play); let profile = self.gameprofile.lock().await.clone(); let profile = profile.unwrap(); let address = self.address.lock().await; if let Some(reason) = can_not_join(&profile, &address, server).await { self.kick(reason).await; return; } let config = self.config.lock().await; if let Some((player, world)) = server .add_player(ClientPlatform::Java(self.clone()), profile, config.clone()) .await { world .spawn_java_player(&server.basic_config, player.clone(), server) .await; *self.player.lock().await = Some(player); } } }