From 0995329a93d2691026c9ae05d9777939f1513120 Mon Sep 17 00:00:00 2001 From: lukas0008 Date: Sun, 18 Aug 2024 21:00:24 +0200 Subject: [PATCH] Move client outside of mod.rs file --- pumpkin/src/client/client.rs | 432 ++++++++++++++++++++++++++ pumpkin/src/client/client_packet.rs | 8 +- pumpkin/src/client/mod.rs | 450 +--------------------------- pumpkin/src/client/player_packet.rs | 11 +- pumpkin/src/commands/gamemode.rs | 4 +- pumpkin/src/commands/mod.rs | 10 +- pumpkin/src/main.rs | 2 + 7 files changed, 460 insertions(+), 457 deletions(-) create mode 100644 pumpkin/src/client/client.rs diff --git a/pumpkin/src/client/client.rs b/pumpkin/src/client/client.rs new file mode 100644 index 000000000..5bcd37982 --- /dev/null +++ b/pumpkin/src/client/client.rs @@ -0,0 +1,432 @@ +use std::{collections::VecDeque, net::SocketAddr, sync::Arc, time::Duration}; + +use crate::{ + entity::player::{ChatMode, GameMode, Hand, Player}, + server::Server, +}; + +use super::connection::Connection; +use super::{authentication::GameProfile, EncryptionError}; +use bytes::BytesMut; +use num_traits::ToPrimitive; +use pumpkin_protocol::{ + bytebuf::packet_id::Packet, + client::{ + config::CConfigDisconnect, + login::CLoginDisconnect, + play::{CGameEvent, CPlayDisconnect, CSyncPlayerPostion, CSystemChatMessge}, + }, + server::{ + config::{SAcknowledgeFinishConfig, SClientInformationConfig, SKnownPacks, SPluginMessage}, + handshake::SHandShake, + login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, + play::{ + SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, + SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, + SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, + }, + status::{SPingRequest, SStatusRequest}, + }, + ClientPacket, ConnectionState, RawPacket, ServerPacket, +}; +use pumpkin_text::TextComponent; +use tokio::{io::AsyncReadExt, net::TcpStream, select, sync::RwLock}; + +pub struct PlayerConfig { + pub locale: String, // 16 + pub view_distance: i8, + pub chat_mode: ChatMode, + pub chat_colors: bool, + pub skin_parts: u8, + pub main_hand: Hand, + pub text_filtering: bool, + pub server_listing: bool, +} + +pub struct Client { + pub player: Option, + + pub gameprofile: Option, + + pub config: Option, + pub brand: Option, + + pub protocol_version: i32, + pub connection_state: ConnectionState, + pub encrytion: bool, + pub closed: bool, + pub token: u32, + pub connection: Connection, + pub address: SocketAddr, + pub client_packets_queue: VecDeque, +} + +impl Client { + pub fn new(token: u32, client: TcpStream, address: SocketAddr) -> Self { + Self { + protocol_version: 0, + gameprofile: None, + config: None, + brand: None, + token, + address, + player: None, + connection_state: ConnectionState::HandShake, + connection: Connection::new(client), + encrytion: true, + closed: false, + client_packets_queue: VecDeque::new(), + } + } + + /// adds a Incoming packet to the queue + pub fn add_packet(&mut self, packet: RawPacket) { + self.client_packets_queue.push_back(packet); + } + + /// enables encryption + pub fn enable_encryption( + &mut self, + shared_secret: &[u8], // decrypted + ) -> Result<(), EncryptionError> { + self.encrytion = true; + let crypt_key: [u8; 16] = shared_secret + .try_into() + .map_err(|_| EncryptionError::SharedWrongLength)?; + self.connection.dec.enable_encryption(&crypt_key); + self.connection.enc.enable_encryption(&crypt_key); + Ok(()) + } + + // Compression threshold, Compression level + pub fn set_compression(&mut self, compression: Option<(u32, u32)>) { + self.connection + .dec + .set_compression(compression.map(|v| v.0)); + self.connection.enc.set_compression(compression); + } + + pub fn is_player(&self) -> bool { + self.player.is_some() + } + + /// Send a Clientbound Packet to the Client + pub async fn send_packet(&mut self, packet: &P) { + match self.connection.try_send_packet(packet).await { + Ok(_) => {} + Err(e) => { + self.kick(&e.to_string()).await; + } + }; + } + + pub async fn teleport(&mut self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) { + assert!(self.is_player()); + // TODO + let id = 0; + let player = self.player.as_mut().unwrap(); + let entity = &mut player.entity; + entity.x = x; + entity.y = y; + entity.z = z; + entity.lastx = x; + entity.lasty = y; + entity.lastz = z; + entity.yaw = yaw; + entity.pitch = pitch; + player.awaiting_teleport = Some(id.into()); + self.send_packet(&CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id.into())) + .await; + } + + pub fn update_health(&mut self, health: f32, food: i32, food_saturation: f32) { + let player = self.player.as_mut().unwrap(); + player.health = health; + player.food = food; + player.food_saturation = food_saturation; + } + + pub async fn set_gamemode(&mut self, gamemode: GameMode) { + let player = self.player.as_mut().unwrap(); + player.gamemode = gamemode; + self.send_packet(&CGameEvent::new(3, gamemode.to_f32().unwrap())) + .await; + } + + pub async fn process_packets(&mut self, server: &mut Server) { + let mut i = 0; + while i < self.client_packets_queue.len() { + let mut packet = self.client_packets_queue.remove(i).unwrap(); + self.handle_packet(server, &mut packet).await; + i += 1; + } + } + + /// Handles an incoming decoded Packet + pub async fn handle_packet(&mut self, server: &mut Server, packet: &mut RawPacket) { + // TODO: handle each packet's Error instead of calling .unwrap() + let bytebuf = &mut packet.bytebuf; + match self.connection_state { + pumpkin_protocol::ConnectionState::HandShake => match packet.id.0 { + SHandShake::PACKET_ID => { + self.handle_handshake(server, SHandShake::read(bytebuf).unwrap()) + .await + } + _ => log::error!( + "Failed to handle packet id {} while in Handshake state", + packet.id.0 + ), + }, + pumpkin_protocol::ConnectionState::Status => match packet.id.0 { + SStatusRequest::PACKET_ID => { + self.handle_status_request(server, SStatusRequest::read(bytebuf).unwrap()) + .await + } + SPingRequest::PACKET_ID => { + self.handle_ping_request(server, SPingRequest::read(bytebuf).unwrap()) + .await + } + _ => log::error!( + "Failed to handle packet id {} while in Status state", + packet.id.0 + ), + }, + pumpkin_protocol::ConnectionState::Login => match packet.id.0 { + SLoginStart::PACKET_ID => { + self.handle_login_start(server, SLoginStart::read(bytebuf).unwrap()) + .await + } + SEncryptionResponse::PACKET_ID => { + self.handle_encryption_response( + server, + SEncryptionResponse::read(bytebuf).unwrap(), + ) + .await + } + SLoginPluginResponse::PACKET_ID => { + self.handle_plugin_response( + server, + SLoginPluginResponse::read(bytebuf).unwrap(), + ) + .await + } + SLoginAcknowledged::PACKET_ID => { + self.handle_login_acknowledged( + server, + SLoginAcknowledged::read(bytebuf).unwrap(), + ) + .await + } + _ => log::error!( + "Failed to handle packet id {} while in Login state", + packet.id.0 + ), + }, + pumpkin_protocol::ConnectionState::Config => match packet.id.0 { + SClientInformationConfig::PACKET_ID => { + self.handle_client_information_config( + server, + SClientInformationConfig::read(bytebuf).unwrap(), + ) + .await + } + SPluginMessage::PACKET_ID => { + self.handle_plugin_message(server, SPluginMessage::read(bytebuf).unwrap()) + .await + } + SAcknowledgeFinishConfig::PACKET_ID => { + self.handle_config_acknowledged( + server, + SAcknowledgeFinishConfig::read(bytebuf).unwrap(), + ) + .await + } + SKnownPacks::PACKET_ID => { + self.handle_known_packs(server, SKnownPacks::read(bytebuf).unwrap()) + .await + } + _ => log::error!( + "Failed to handle packet id {} while in Config state", + packet.id.0 + ), + }, + pumpkin_protocol::ConnectionState::Play => { + if self.player.is_some() { + self.handle_play_packet(server, packet).await; + } else { + // should be impossible + self.kick("no player in play state?").await + } + } + _ => log::error!("Invalid Connection state {:?}", self.connection_state), + } + } + + pub async fn handle_play_packet(&mut self, server: &mut Server, packet: &mut RawPacket) { + let bytebuf = &mut packet.bytebuf; + match packet.id.0 { + SConfirmTeleport::PACKET_ID => { + self.handle_confirm_teleport(server, SConfirmTeleport::read(bytebuf).unwrap()) + .await + } + SChatCommand::PACKET_ID => { + self.handle_chat_command(server, SChatCommand::read(bytebuf).unwrap()) + .await + } + SPlayerPosition::PACKET_ID => { + self.handle_position(server, SPlayerPosition::read(bytebuf).unwrap()) + .await + } + SPlayerPositionRotation::PACKET_ID => { + self.handle_position_rotation( + server, + SPlayerPositionRotation::read(bytebuf).unwrap(), + ) + .await + } + SPlayerRotation::PACKET_ID => { + self.handle_rotation(server, SPlayerRotation::read(bytebuf).unwrap()) + .await + } + SPlayerCommand::PACKET_ID => { + self.handle_player_command(server, SPlayerCommand::read(bytebuf).unwrap()) + .await + } + SSwingArm::PACKET_ID => { + self.handle_swing_arm(server, SSwingArm::read(bytebuf).unwrap()) + .await + } + SChatMessage::PACKET_ID => { + self.handle_chat_message(server, SChatMessage::read(bytebuf).unwrap()) + .await + } + SClientInformationPlay::PACKET_ID => { + self.handle_client_information_play( + server, + SClientInformationPlay::read(bytebuf).unwrap(), + ) + .await + } + SInteract::PACKET_ID => { + self.handle_interact(server, SInteract::read(bytebuf).unwrap()) + .await + } + SPlayerAction::PACKET_ID => { + self.handle_player_action(server, SPlayerAction::read(bytebuf).unwrap()) + .await + } + SUseItemOn::PACKET_ID => { + self.handle_use_item_on(server, SUseItemOn::read(bytebuf).unwrap()) + .await + } + SSetHeldItem::PACKET_ID => { + self.handle_set_held_item(server, SSetHeldItem::read(bytebuf).unwrap()) + .await + } + SSetCreativeSlot::PACKET_ID => { + self.handle_set_creative_slot(server, SSetCreativeSlot::read(bytebuf).unwrap()) + .await + } + _ => log::error!("Failed to handle player packet id {}", packet.id.0), + } + } + + // Reads the connection until our buffer of len 4096 is full, then decode + /// Close connection when an error occurs + pub async fn poll(&mut self, server: Arc>) { + dbg!("b"); + + let mut buf = BytesMut::new(); + loop { + select! { + result = self.connection.client.read_buf(&mut buf) => { + match result { + Ok(0) => { + self.close(); + break; + } + Ok(_) => { + self.connection.dec.queue_bytes(buf.split()); + } + Err(e) => { + log::error!("{}", e); + self.kick(&e.to_string()).await; + break; + } + }; + loop { + match self.connection.dec.decode() { + Ok(Some(packet)) => { + self.add_packet(packet); + let mut server = server.write().await; + self.process_packets(&mut server).await; + continue; + } + Ok(None) => break, + Err(err) => { + self.kick(&err.to_string()).await; + break; + }, + }; + } + + }, + _ = tokio::time::sleep(Duration::from_millis(100)) => { + // Handle timeout (optional) + } + } + } + } + + pub async fn send_system_message<'a>(&mut self, text: TextComponent<'a>) { + self.send_packet(&CSystemChatMessge::new(text, false)).await; + } + + /// Kicks the Client with a reason depending on the connection state + pub async fn kick(&mut self, reason: &str) { + dbg!(reason); + match self.connection_state { + ConnectionState::Login => { + match self + .connection + .try_send_packet(&CLoginDisconnect::new( + &serde_json::to_string_pretty(&reason).unwrap(), + )) + .await + { + Ok(_) => {} + Err(_) => self.close(), + } + } + ConnectionState::Config => { + match self + .connection + .try_send_packet(&CConfigDisconnect::new(reason)) + .await + { + Ok(_) => {} + Err(_) => self.close(), + } + } + ConnectionState::Play => { + match self + .connection + .try_send_packet(&CPlayDisconnect::new(TextComponent::text(reason))) + .await + { + Ok(_) => {} + Err(_) => self.close(), + } + } + _ => { + log::warn!("Cant't kick in {:?} State", self.connection_state) + } + } + self.close() + } + + /// You should prefer to use `kick` when you can + pub fn close(&mut self) { + self.closed = true; + } +} diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index d19f55a0a..4e2754a09 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -18,7 +18,10 @@ use rsa::Pkcs1v15Encrypt; use sha1::{Digest, Sha1}; use crate::{ - client::authentication::{self, GameProfile}, + client::{ + authentication::{self, GameProfile}, + client::PlayerConfig, + }, entity::player::{ChatMode, Hand}, proxy::velocity::velocity_login, server::{Server, CURRENT_MC_VERSION}, @@ -26,7 +29,8 @@ use crate::{ use super::{ authentication::{auth_digest, unpack_textures}, - Client, EncryptionError, PlayerConfig, + client::Client, + EncryptionError, }; /// Processes incoming Packets from the Client to the Server diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 9b536d511..25309b0ec 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -1,455 +1,14 @@ -use std::{ - collections::VecDeque, - io::{self, Write}, - net::SocketAddr, - sync::Arc, - time::Duration, -}; +use std::io; -use crate::{ - entity::player::{ChatMode, GameMode, Hand, Player}, - server::Server, -}; - -use authentication::GameProfile; -use bytes::BytesMut; -use connection::Connection; -use num_traits::ToPrimitive; -use pumpkin_protocol::{ - bytebuf::packet_id::Packet, - client::{ - config::CConfigDisconnect, - login::CLoginDisconnect, - play::{CGameEvent, CPlayDisconnect, CSyncPlayerPostion, CSystemChatMessge}, - }, - packet_decoder::PacketDecoder, - packet_encoder::PacketEncoder, - server::{ - config::{SAcknowledgeFinishConfig, SClientInformationConfig, SKnownPacks, SPluginMessage}, - handshake::SHandShake, - login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, - play::{ - SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, - SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, - SPlayerRotation, SSetCreativeSlot, SSetHeldItem, SSwingArm, SUseItemOn, - }, - status::{SPingRequest, SStatusRequest}, - }, - ClientPacket, ConnectionState, PacketError, RawPacket, ServerPacket, -}; -use pumpkin_text::TextComponent; -use tokio::{ - io::{AsyncReadExt, AsyncWriteExt}, - net::TcpStream, - select, - sync::RwLock, -}; - -use std::io::Read; use thiserror::Error; - pub mod authentication; +mod client; +pub use client::*; mod client_packet; pub mod connection; pub mod player_packet; -pub struct PlayerConfig { - pub locale: String, // 16 - pub view_distance: i8, - pub chat_mode: ChatMode, - pub chat_colors: bool, - pub skin_parts: u8, - pub main_hand: Hand, - pub text_filtering: bool, - pub server_listing: bool, -} - -pub struct Client { - pub player: Option, - - pub gameprofile: Option, - - pub config: Option, - pub brand: Option, - - pub protocol_version: i32, - pub connection_state: ConnectionState, - pub encryption: bool, - pub closed: bool, - pub token: u32, - pub connection: Connection, - pub address: SocketAddr, - pub client_packets_queue: VecDeque, -} - -impl Client { - pub fn new(token: u32, client: TcpStream, address: SocketAddr) -> Self { - Self { - protocol_version: 0, - gameprofile: None, - config: None, - brand: None, - token, - address, - player: None, - connection_state: ConnectionState::HandShake, - connection: Connection::new(client), - encryption: true, - closed: false, - client_packets_queue: VecDeque::new(), - } - } - - /// adds a Incoming packet to the queue - pub fn add_packet(&mut self, packet: RawPacket) { - self.client_packets_queue.push_back(packet); - } - - /// enables encryption - pub fn enable_encryption( - &mut self, - shared_secret: &[u8], // decrypted - ) -> Result<(), EncryptionError> { - self.encryption = true; - let crypt_key: [u8; 16] = shared_secret - .try_into() - .map_err(|_| EncryptionError::SharedWrongLength)?; - self.connection.dec.enable_encryption(&crypt_key); - self.connection.enc.enable_encryption(&crypt_key); - Ok(()) - } - - // Compression threshold, Compression level - pub fn set_compression(&mut self, compression: Option<(u32, u32)>) { - self.connection - .dec - .set_compression(compression.map(|v| v.0)); - self.connection.enc.set_compression(compression); - } - - pub fn is_player(&self) -> bool { - self.player.is_some() - } - - /// Send a Clientbound Packet to the Client - pub async fn send_packet(&mut self, packet: &P) { - match self.connection.try_send_packet(packet).await { - Ok(_) => {} - Err(e) => { - self.kick(&e.to_string()).await; - } - }; - } - - pub async fn teleport(&mut self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) { - assert!(self.is_player()); - // TODO - let id = 0; - let player = self.player.as_mut().unwrap(); - let entity = &mut player.entity; - entity.x = x; - entity.y = y; - entity.z = z; - entity.lastx = x; - entity.lasty = y; - entity.lastz = z; - entity.yaw = yaw; - entity.pitch = pitch; - player.awaiting_teleport = Some(id.into()); - self.send_packet(&CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id.into())) - .await; - } - - pub fn update_health(&mut self, health: f32, food: i32, food_saturation: f32) { - let player = self.player.as_mut().unwrap(); - player.health = health; - player.food = food; - player.food_saturation = food_saturation; - } - - pub fn set_gamemode(&mut self, gamemode: GameMode) { - let player = self.player.as_mut().unwrap(); - player.gamemode = gamemode; - self.send_packet(&CGameEvent::new(3, gamemode.to_f32().unwrap())); - } - - pub async fn process_packets(&mut self, server: &mut Server) { - let mut i = 0; - while i < self.client_packets_queue.len() { - let mut packet = self.client_packets_queue.remove(i).unwrap(); - self.handle_packet(server, &mut packet).await; - i += 1; - } - } - - /// Handles an incoming decoded Packet - pub async fn handle_packet(&mut self, server: &mut Server, packet: &mut RawPacket) { - // TODO: handle each packet's Error instead of calling .unwrap() - let bytebuf = &mut packet.bytebuf; - match self.connection_state { - pumpkin_protocol::ConnectionState::HandShake => match packet.id.0 { - SHandShake::PACKET_ID => { - self.handle_handshake(server, SHandShake::read(bytebuf).unwrap()) - .await - } - _ => log::error!( - "Failed to handle packet id {} while in Handshake state", - packet.id.0 - ), - }, - pumpkin_protocol::ConnectionState::Status => match packet.id.0 { - SStatusRequest::PACKET_ID => { - self.handle_status_request(server, SStatusRequest::read(bytebuf).unwrap()) - .await - } - SPingRequest::PACKET_ID => { - self.handle_ping_request(server, SPingRequest::read(bytebuf).unwrap()) - .await - } - _ => log::error!( - "Failed to handle packet id {} while in Status state", - packet.id.0 - ), - }, - pumpkin_protocol::ConnectionState::Login => match packet.id.0 { - SLoginStart::PACKET_ID => { - self.handle_login_start(server, SLoginStart::read(bytebuf).unwrap()) - .await - } - SEncryptionResponse::PACKET_ID => { - self.handle_encryption_response( - server, - SEncryptionResponse::read(bytebuf).unwrap(), - ) - .await - } - SLoginPluginResponse::PACKET_ID => { - self.handle_plugin_response( - server, - SLoginPluginResponse::read(bytebuf).unwrap(), - ) - .await - } - SLoginAcknowledged::PACKET_ID => { - self.handle_login_acknowledged( - server, - SLoginAcknowledged::read(bytebuf).unwrap(), - ) - .await - } - _ => log::error!( - "Failed to handle packet id {} while in Login state", - packet.id.0 - ), - }, - pumpkin_protocol::ConnectionState::Config => match packet.id.0 { - SClientInformationConfig::PACKET_ID => { - self.handle_client_information_config( - server, - SClientInformationConfig::read(bytebuf).unwrap(), - ) - .await - } - SPluginMessage::PACKET_ID => { - self.handle_plugin_message(server, SPluginMessage::read(bytebuf).unwrap()) - .await - } - SAcknowledgeFinishConfig::PACKET_ID => { - self.handle_config_acknowledged( - server, - SAcknowledgeFinishConfig::read(bytebuf).unwrap(), - ) - .await - } - SKnownPacks::PACKET_ID => { - self.handle_known_packs(server, SKnownPacks::read(bytebuf).unwrap()) - .await - } - _ => log::error!( - "Failed to handle packet id {} while in Config state", - packet.id.0 - ), - }, - pumpkin_protocol::ConnectionState::Play => { - if self.player.is_some() { - self.handle_play_packet(server, packet).await; - } else { - // should be impossible - self.kick("no player in play state?").await - } - } - _ => log::error!("Invalid Connection state {:?}", self.connection_state), - } - } - - pub async fn handle_play_packet(&mut self, server: &mut Server, packet: &mut RawPacket) { - let bytebuf = &mut packet.bytebuf; - match packet.id.0 { - SConfirmTeleport::PACKET_ID => { - self.handle_confirm_teleport(server, SConfirmTeleport::read(bytebuf).unwrap()) - .await - } - SChatCommand::PACKET_ID => { - self.handle_chat_command(server, SChatCommand::read(bytebuf).unwrap()) - .await - } - SPlayerPosition::PACKET_ID => { - self.handle_position(server, SPlayerPosition::read(bytebuf).unwrap()) - .await - } - SPlayerPositionRotation::PACKET_ID => { - self.handle_position_rotation( - server, - SPlayerPositionRotation::read(bytebuf).unwrap(), - ) - .await - } - SPlayerRotation::PACKET_ID => { - self.handle_rotation(server, SPlayerRotation::read(bytebuf).unwrap()) - .await - } - SPlayerCommand::PACKET_ID => { - self.handle_player_command(server, SPlayerCommand::read(bytebuf).unwrap()) - .await - } - SSwingArm::PACKET_ID => { - self.handle_swing_arm(server, SSwingArm::read(bytebuf).unwrap()) - .await - } - SChatMessage::PACKET_ID => { - self.handle_chat_message(server, SChatMessage::read(bytebuf).unwrap()) - .await - } - SClientInformationPlay::PACKET_ID => { - self.handle_client_information_play( - server, - SClientInformationPlay::read(bytebuf).unwrap(), - ) - .await - } - SInteract::PACKET_ID => { - self.handle_interact(server, SInteract::read(bytebuf).unwrap()) - .await - } - SPlayerAction::PACKET_ID => { - self.handle_player_action(server, SPlayerAction::read(bytebuf).unwrap()) - .await - } - SUseItemOn::PACKET_ID => { - self.handle_use_item_on(server, SUseItemOn::read(bytebuf).unwrap()) - .await - } - SSetHeldItem::PACKET_ID => { - self.handle_set_held_item(server, SSetHeldItem::read(bytebuf).unwrap()) - .await - } - SSetCreativeSlot::PACKET_ID => { - self.handle_set_creative_slot(server, SSetCreativeSlot::read(bytebuf).unwrap()) - .await - } - _ => log::error!("Failed to handle player packet id {}", packet.id.0), - } - } - - // Reads the connection until our buffer of len 4096 is full, then decode - /// Close connection when an error occurs - pub async fn poll(&mut self, server: Arc>) { - dbg!("b"); - - let mut buf = BytesMut::new(); - loop { - select! { - result = self.connection.client.read_buf(&mut buf) => { - match result { - Ok(0) => { - self.close(); - break; - } - Ok(_) => { - self.connection.dec.queue_bytes(buf.split()); - } - Err(e) => { - log::error!("{}", e); - self.kick(&e.to_string()).await; - break; - } - }; - loop { - match self.connection.dec.decode() { - Ok(Some(packet)) => { - self.add_packet(packet); - let mut server = server.write().await; - self.process_packets(&mut server).await; - continue; - } - Ok(None) => break, - Err(err) => { - self.kick(&err.to_string()).await; - break; - }, - }; - } - - }, - _ = tokio::time::sleep(Duration::from_millis(100)) => { - // Handle timeout (optional) - } - } - } - } - - pub async fn send_system_message(&mut self, text: TextComponent) { - self.send_packet(&CSystemChatMessge::new(text, false)).await; - } - - /// Kicks the Client with a reason depending on the connection state - pub async fn kick(&mut self, reason: &str) { - dbg!(reason); - match self.connection_state { - ConnectionState::Login => { - match self - .connection - .try_send_packet(&CLoginDisconnect::new( - &serde_json::to_string_pretty(&reason).unwrap(), - )) - .await - { - Ok(_) => {} - Err(_) => self.close(), - } - } - ConnectionState::Config => { - match self - .connection - .try_send_packet(&CConfigDisconnect::new(reason)) - .await - { - Ok(_) => {} - Err(_) => self.close(), - } - } - ConnectionState::Play => { - match self - .connection - .try_send_packet(&CPlayDisconnect::new(TextComponent::text(reason))) - .await - { - Ok(_) => {} - Err(_) => self.close(), - } - } - _ => { - log::warn!("Can't kick in {:?} State", self.connection_state) - } - } - self.close() - } - - /// You should prefer to use `kick` when you can - pub fn close(&mut self) { - self.closed = true; - } -} +pub use client::Client; #[derive(Error, Debug)] pub enum EncryptionError { @@ -459,6 +18,7 @@ pub enum EncryptionError { SharedWrongLength, } +#[allow(dead_code)] fn would_block(err: &io::Error) -> bool { err.kind() == io::ErrorKind::WouldBlock } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 833b9546e..d30e04486 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -4,8 +4,7 @@ use num_traits::FromPrimitive; use pumpkin_entity::EntityId; use pumpkin_protocol::{ client::play::{ - Animation, CBlockUpdate, CEntityAnimation, CEntityVelocity, CHeadRot, CHurtAnimation, - CPlayerChatMessage, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot, FilterType, + Animation, CBlockUpdate, CEntityAnimation, CEntityVelocity, CHeadRot, CHurtAnimation, CPlayerChatMessage, CSystemChatMessge, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot, FilterType }, position::WorldPosition, server::play::{ @@ -24,7 +23,7 @@ use crate::{ util::math::wrap_degrees, }; -use super::{Client, PlayerConfig}; +use super::{client::PlayerConfig, Client}; fn modulus(a: f32, b: f32) -> f32 { ((a % b) + b) % b @@ -226,18 +225,20 @@ impl Client { let message = chat_message.message; // TODO: filter message & validation let gameprofile = self.gameprofile.as_ref().unwrap(); + let name = gameprofile.name.clone(); dbg!("got message"); // yeah a "raw system message", the ugly way to do that, but it works server .broadcast_packet( self, &CSystemChatMessge::new( - TextComponent::from(format!("{}: {}", gameprofile.name, message)), + TextComponent::text(&format!("{}: {}", name, message)), false, ), ) .await; + let gameprofile = self.gameprofile.as_ref().unwrap(); server.broadcast_packet( self, &CPlayerChatMessage::new( @@ -296,7 +297,7 @@ impl Client { let attacker_player = self.player.as_mut().unwrap(); attacker_player.sneaking = interact.sneaking; if let Some(mut client) = attacked_client { - let token = client.token.clone(); + let token = client.token; let player = client.player.as_mut().unwrap(); let velo = player.velocity; if config.protect_creative && player.gamemode == GameMode::Creative { diff --git a/pumpkin/src/commands/gamemode.rs b/pumpkin/src/commands/gamemode.rs index e16c94c40..b96036617 100644 --- a/pumpkin/src/commands/gamemode.rs +++ b/pumpkin/src/commands/gamemode.rs @@ -29,7 +29,7 @@ impl<'a> Command<'a> for GamemodeCommand { let mode_str = args[1].to_lowercase(); match mode_str.parse() { Ok(mode) => { - player.set_gamemode(mode); + player.set_gamemode(mode).await; player .send_system_message(TextComponent::text(&format!( "Set own game mode to {:?}", @@ -41,7 +41,7 @@ impl<'a> Command<'a> for GamemodeCommand { // try to parse from number if let Ok(i) = mode_str.parse::() { if let Some(mode) = GameMode::from_u8(i) { - player.set_gamemode(mode); + player.set_gamemode(mode).await; player.send_system_message(TextComponent::text(&format!( "Set own game mode to {:?}", mode diff --git a/pumpkin/src/commands/mod.rs b/pumpkin/src/commands/mod.rs index 7f32a0b47..d177c5196 100644 --- a/pumpkin/src/commands/mod.rs +++ b/pumpkin/src/commands/mod.rs @@ -1,3 +1,5 @@ +use std::future::Future; + use gamemode::GamemodeCommand; use pumpkin::PumpkinCommand; use pumpkin_text::TextComponent; @@ -15,7 +17,7 @@ pub trait Command<'a> { const NAME: &'a str; const DESCRIPTION: &'a str; - async fn on_execute(sender: &mut CommandSender<'a>, command: String); + fn on_execute(sender: &mut CommandSender<'a>, command: String) -> impl Future; /// Specifies wether the Command Sender has to be a Player /// TODO: implement @@ -31,7 +33,7 @@ pub enum CommandSender<'a> { } impl<'a> CommandSender<'a> { - pub async fn send_message(&mut self, text: TextComponent) { + pub async fn send_message<'b>(&mut self, text: TextComponent<'b>) { match self { // TODO: add color and stuff to console CommandSender::Console => log::info!("{:?}", text.content), @@ -79,5 +81,7 @@ pub async fn handle_command<'a>(sender: &mut CommandSender<'a>, command: &str) { return; } // TODO: red color - sender.send_message(TextComponent::text("Command not Found")).await; + sender + .send_message(TextComponent::text("Command not Found")) + .await; } diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 68f05700d..65975d4ef 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::module_inception)] + use std::{ io::{self}, net::SocketAddr,