238 lines
9.3 KiB
Rust
238 lines
9.3 KiB
Rust
use std::{num::NonZeroU8, sync::Arc};
|
|
|
|
use crate::{
|
|
entity::player::ChatMode,
|
|
net::{
|
|
PlayerConfig, can_not_join,
|
|
java::{JavaClient, PacketHandlerResult},
|
|
},
|
|
server::Server,
|
|
};
|
|
use core::str;
|
|
use pumpkin_data::registry::Registry;
|
|
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, version::MinecraftVersion};
|
|
use tracing::{debug, trace, warn};
|
|
|
|
const BRAND_CHANNEL_PREFIX: &str = "minecraft:brand";
|
|
|
|
impl JavaClient {
|
|
pub async fn handle_client_information_config(
|
|
&self,
|
|
client_information: SClientInformationConfig,
|
|
) {
|
|
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) {
|
|
debug!("Handling plugin message");
|
|
if plugin_message.channel.starts_with(BRAND_CHANNEL_PREFIX) {
|
|
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 => {
|
|
trace!(
|
|
"Client {} successfully downloaded the resource pack",
|
|
self.id
|
|
);
|
|
}
|
|
ResourcePackResponseResult::DownloadFail => {
|
|
warn!(
|
|
"Client {} failed to downloaded the resource pack. Is it available on the internet?",
|
|
self.id
|
|
);
|
|
}
|
|
ResourcePackResponseResult::Downloaded => {
|
|
trace!("Client {} already has the resource pack", self.id);
|
|
}
|
|
ResourcePackResponseResult::Accepted => {
|
|
trace!("Client {} accepted the resource pack", self.id);
|
|
|
|
// Return here to wait for the next response update
|
|
return;
|
|
}
|
|
ResourcePackResponseResult::Declined => {
|
|
trace!("Client {} declined the resource pack", self.id);
|
|
}
|
|
ResourcePackResponseResult::InvalidUrl => {
|
|
warn!(
|
|
"Client {} reported that the resource pack URL is invalid!",
|
|
self.id
|
|
);
|
|
}
|
|
ResourcePackResponseResult::ReloadFailed => {
|
|
trace!("Client {} failed to reload the resource pack", self.id);
|
|
}
|
|
ResourcePackResponseResult::Discarded => {
|
|
trace!("Client {} discarded the resource pack", self.id);
|
|
}
|
|
ResourcePackResponseResult::Unknown(result) => {
|
|
warn!(
|
|
"Client {} responded with a bad result: {}!",
|
|
self.id, result
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
warn!(
|
|
"Client {} returned a response for a resource pack we did not set!",
|
|
self.id
|
|
);
|
|
}
|
|
} else {
|
|
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
|
|
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) {
|
|
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(®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;
|
|
let mut tags = vec![
|
|
pumpkin_data::tag::RegistryKey::Block,
|
|
pumpkin_data::tag::RegistryKey::Fluid,
|
|
pumpkin_data::tag::RegistryKey::Enchantment,
|
|
pumpkin_data::tag::RegistryKey::WorldgenBiome,
|
|
pumpkin_data::tag::RegistryKey::Item,
|
|
pumpkin_data::tag::RegistryKey::EntityType,
|
|
pumpkin_data::tag::RegistryKey::Dialog,
|
|
];
|
|
|
|
// optionally include timeline/dimension_type if there are any tags to send
|
|
if self.version.load().protocol_version() >= MinecraftVersion::V_1_21_11.protocol_version()
|
|
&& let Some(map) = pumpkin_data::tag::get_registry_key_tags(
|
|
self.version.load(),
|
|
pumpkin_data::tag::RegistryKey::Timeline,
|
|
)
|
|
&& !map.is_empty()
|
|
{
|
|
tags.push(pumpkin_data::tag::RegistryKey::Timeline);
|
|
}
|
|
if let Some(map) = pumpkin_data::tag::get_registry_key_tags(
|
|
self.version.load(),
|
|
pumpkin_data::tag::RegistryKey::DimensionType,
|
|
) && !map.is_empty()
|
|
{
|
|
tags.push(pumpkin_data::tag::RegistryKey::DimensionType);
|
|
}
|
|
if let Some(map) = pumpkin_data::tag::get_registry_key_tags(
|
|
self.version.load(),
|
|
pumpkin_data::tag::RegistryKey::DamageType,
|
|
) && !map.is_empty()
|
|
{
|
|
tags.push(pumpkin_data::tag::RegistryKey::DamageType);
|
|
}
|
|
if let Some(map) = pumpkin_data::tag::get_registry_key_tags(
|
|
self.version.load(),
|
|
pumpkin_data::tag::RegistryKey::BannerPattern,
|
|
) && !map.is_empty()
|
|
{
|
|
tags.push(pumpkin_data::tag::RegistryKey::BannerPattern);
|
|
}
|
|
self.send_packet_now(&CUpdateTags::new(&tags)).await;
|
|
|
|
// We are done with configuring
|
|
debug!("Finished config");
|
|
self.send_packet_now(&CFinishConfig).await;
|
|
}
|
|
|
|
pub async fn handle_config_acknowledged(&self, server: &Arc<Server>) -> PacketHandlerResult {
|
|
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 PacketHandlerResult::Stop;
|
|
}
|
|
|
|
let config = self.config.lock().await;
|
|
PacketHandlerResult::ReadyToPlay(profile, config.clone().unwrap_or_default())
|
|
}
|
|
}
|