Pumpkin/pumpkin/src/net/java/config.rs
Alexander Medvedev 142296a9b2 feat: 1.21.9-1.21.11 support
more versions will come
2026-01-22 17:16:37 +01:00

204 lines
7.8 KiB
Rust

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<RegistryEntry> = registry
.registry_entries
.iter()
.map(|r| RegistryEntry::new(r.entry_id.clone(), r.data.clone()))
.collect();
self.send_packet_now(&CRegistryData::new(&registry.registry_id, &entries))
.await;
if let Some(tag) = RegistryKey::from_string(&registry.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<Self>, 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);
}
}
}