From fe8cd334e1715b9b7ec93796f7ac411c5c2c20fb Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Thu, 8 Aug 2024 11:27:09 +0200 Subject: [PATCH] Add Chat support --- README.md | 2 +- pumpkin-protocol/src/bytebuf/serializer.rs | 17 +++---- .../src/client/login/c_encryption_request.rs | 27 ++++------ .../client/play/c_disguised_chat_message.rs | 37 ++++++++++++++ pumpkin-protocol/src/client/play/c_login.rs | 2 + .../src/client/play/c_player_chat_message.rs | 12 +++-- .../src/client/play/c_system_chat_message.rs | 2 +- pumpkin-protocol/src/client/play/mod.rs | 2 + pumpkin-protocol/src/lib.rs | 10 ++++ pumpkin-protocol/src/server/play/mod.rs | 2 + .../src/server/play/s_chat_message.rs | 29 +++++++++++ pumpkin-protocol/src/text.rs | 2 +- pumpkin/src/client/authentication.rs | 1 - pumpkin/src/client/client_packet.rs | 6 +-- pumpkin/src/client/mod.rs | 16 ++++-- pumpkin/src/client/player_packet.rs | 50 +++++++++++++++++-- pumpkin/src/commands/mod.rs | 1 + pumpkin/src/server.rs | 1 + 18 files changed, 171 insertions(+), 48 deletions(-) create mode 100644 pumpkin-protocol/src/client/play/c_disguised_chat_message.rs create mode 100644 pumpkin-protocol/src/server/play/s_chat_message.rs diff --git a/README.md b/README.md index b5dc4b869..3eb27fbcf 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Pumpkin is currently under heavy development. - [ ] Player Inventory - [ ] Player Attack - Server - - [ ] Chat + - [x] Chat - [x] Commands Check out our [Github Project](https://github.com/users/Snowiiii/projects/12/views/1) to see current progress diff --git a/pumpkin-protocol/src/bytebuf/serializer.rs b/pumpkin-protocol/src/bytebuf/serializer.rs index f3c81f5d7..446c87650 100644 --- a/pumpkin-protocol/src/bytebuf/serializer.rs +++ b/pumpkin-protocol/src/bytebuf/serializer.rs @@ -1,10 +1,11 @@ use std::fmt::Display; -use serde::{ser, Serialize}; +use serde::{ + ser::{self}, + Serialize, +}; use thiserror::Error; -use crate::VarInt; - use super::ByteBuffer; pub struct Serializer { @@ -126,10 +127,9 @@ impl<'a> ser::Serializer for &'a mut Serializer { self.output.put_bool(false); Ok(()) } - fn serialize_seq(self, len: Option) -> Result { - if let Some(len) = len { - self.output.put_var_int(&VarInt(len as i32)); - } + fn serialize_seq(self, _len: Option) -> Result { + // here is where all arrays/list getting written, usally we prefix the length of every length with an var int. The problem is + // that byte arrays also getting thrown in here, and we don't want to prefix them Ok(self) } fn serialize_some(self, value: &T) -> Result @@ -137,7 +137,6 @@ impl<'a> ser::Serializer for &'a mut Serializer { T: ?Sized + Serialize, { self.output.put_bool(true); - dbg!("aa"); value.serialize(self) } fn serialize_str(self, v: &str) -> Result { @@ -288,7 +287,7 @@ impl<'a> ser::SerializeTupleVariant for &'a mut Serializer { } fn end(self) -> Result<(), Self::Error> { - todo!() + Ok(()) } } diff --git a/pumpkin-protocol/src/client/login/c_encryption_request.rs b/pumpkin-protocol/src/client/login/c_encryption_request.rs index 312b5cd28..a7f0a7f01 100644 --- a/pumpkin-protocol/src/client/login/c_encryption_request.rs +++ b/pumpkin-protocol/src/client/login/c_encryption_request.rs @@ -1,42 +1,33 @@ use pumpkin_macros::packet; +use serde::Serialize; -use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt}; +use crate::{RawBytes, VarInt}; +#[derive(Serialize)] #[packet(0x01)] pub struct CEncryptionRequest<'a> { server_id: &'a str, // 20 public_key_length: VarInt, - public_key: &'a [u8], + public_key: RawBytes<'a>, verify_token_length: VarInt, - verify_token: &'a [u8], + verify_token: RawBytes<'a>, should_authenticate: bool, } impl<'a> CEncryptionRequest<'a> { pub fn new( server_id: &'a str, - public_key: &'a [u8], - verify_token: &'a [u8], + public_key: RawBytes<'a>, + verify_token: RawBytes<'a>, should_authenticate: bool, ) -> Self { Self { server_id, - public_key_length: public_key.len().into(), + public_key_length: public_key.0.len().into(), public_key, - verify_token_length: verify_token.len().into(), + verify_token_length: verify_token.0.len().into(), verify_token, should_authenticate, } } } - -impl<'a> ClientPacket for CEncryptionRequest<'a> { - fn write(&self, bytebuf: &mut ByteBuffer) { - bytebuf.put_string(self.server_id); - bytebuf.put_var_int(&self.public_key_length); - bytebuf.put_slice(self.public_key); - bytebuf.put_var_int(&self.verify_token_length); - bytebuf.put_slice(self.verify_token); - bytebuf.put_bool(self.should_authenticate); - } -} diff --git a/pumpkin-protocol/src/client/play/c_disguised_chat_message.rs b/pumpkin-protocol/src/client/play/c_disguised_chat_message.rs new file mode 100644 index 000000000..beff080d7 --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_disguised_chat_message.rs @@ -0,0 +1,37 @@ +use pumpkin_macros::packet; + +use crate::{text::TextComponent, ClientPacket, VarInt}; + +#[derive(Clone)] +#[packet(0x1E)] +pub struct CDisguisedChatMessage { + message: TextComponent, + chat_type: VarInt, + sender_name: TextComponent, + target_name: Option, +} + +impl CDisguisedChatMessage { + pub fn new( + message: TextComponent, + chat_type: VarInt, + sender_name: TextComponent, + target_name: Option, + ) -> Self { + Self { + message, + chat_type, + sender_name, + target_name, + } + } +} + +impl ClientPacket for CDisguisedChatMessage { + fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) { + bytebuf.put_slice(&self.message.encode()); + bytebuf.put_var_int(&self.chat_type); + bytebuf.put_slice(&self.sender_name.encode()); + bytebuf.put_option(&self.target_name, |p, v| p.put_slice(&v.encode())); + } +} diff --git a/pumpkin-protocol/src/client/play/c_login.rs b/pumpkin-protocol/src/client/play/c_login.rs index ad18d6e74..4ad679bbf 100644 --- a/pumpkin-protocol/src/client/play/c_login.rs +++ b/pumpkin-protocol/src/client/play/c_login.rs @@ -8,6 +8,7 @@ use crate::VarInt; pub struct CLogin { entity_id: i32, is_hardcore: bool, + dimension_count: VarInt, dimension_names: Vec, max_players: VarInt, view_distance: VarInt, @@ -53,6 +54,7 @@ impl CLogin { Self { entity_id, is_hardcore, + dimension_count: VarInt(dimension_names.len() as i32), dimension_names, max_players, view_distance, diff --git a/pumpkin-protocol/src/client/play/c_player_chat_message.rs b/pumpkin-protocol/src/client/play/c_player_chat_message.rs index cfbdb63ca..927e0ebbc 100644 --- a/pumpkin-protocol/src/client/play/c_player_chat_message.rs +++ b/pumpkin-protocol/src/client/play/c_player_chat_message.rs @@ -1,10 +1,12 @@ -use num_derive::FromPrimitive; +use num_derive::{FromPrimitive, ToPrimitive}; +use pumpkin_macros::packet; use serde::Serialize; use crate::{text::TextComponent, VarInt}; -#[derive(Serialize)] -pub struct PlayerChatMessage<'a> { +#[derive(Serialize, Clone)] +#[packet(0x39)] +pub struct CPlayerChatMessage<'a> { sender: uuid::Uuid, index: VarInt, message_signature: Option<&'a [u8]>, @@ -20,7 +22,7 @@ pub struct PlayerChatMessage<'a> { target_name: Option, } -impl<'a> PlayerChatMessage<'a> { +impl<'a> CPlayerChatMessage<'a> { #[allow(clippy::too_many_arguments)] pub fn new( sender: uuid::Uuid, @@ -59,7 +61,7 @@ pub struct PreviousMessage<'a> { signature: Option<&'a [u8]>, } -#[derive(FromPrimitive)] +#[derive(FromPrimitive, ToPrimitive)] pub enum FilterType { /// Message is not filtered at all PassThrough, diff --git a/pumpkin-protocol/src/client/play/c_system_chat_message.rs b/pumpkin-protocol/src/client/play/c_system_chat_message.rs index 2e5efedf2..9c68b5860 100644 --- a/pumpkin-protocol/src/client/play/c_system_chat_message.rs +++ b/pumpkin-protocol/src/client/play/c_system_chat_message.rs @@ -3,7 +3,7 @@ use serde::Serialize; use crate::text::TextComponent; -#[derive(Serialize)] +#[derive(Serialize, Clone)] #[packet(0x6C)] pub struct CSystemChatMessge { content: TextComponent, diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index f5ba00d37..c120926ee 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -1,5 +1,6 @@ mod c_change_difficulty; mod c_chunk_data_update_light; +mod c_disguised_chat_message; mod c_entity_animation; mod c_entity_metadata; mod c_game_event; @@ -21,6 +22,7 @@ mod player_action; pub use c_change_difficulty::*; pub use c_chunk_data_update_light::*; +pub use c_disguised_chat_message::*; pub use c_entity_animation::*; pub use c_entity_metadata::*; pub use c_game_event::*; diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index fe2eec998..02a34cc27 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -91,6 +91,16 @@ impl VarInt { } } +pub struct RawBytes<'a>(pub &'a [u8]); +impl<'a> Serialize for RawBytes<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_bytes(self.0) + } +} + impl From for VarInt { fn from(value: i32) -> Self { VarInt(value) diff --git a/pumpkin-protocol/src/server/play/mod.rs b/pumpkin-protocol/src/server/play/mod.rs index 820f45a2a..fe2fe0274 100644 --- a/pumpkin-protocol/src/server/play/mod.rs +++ b/pumpkin-protocol/src/server/play/mod.rs @@ -1,4 +1,5 @@ mod s_chat_command; +mod s_chat_message; mod s_confirm_teleport; mod s_player_command; mod s_player_position; @@ -7,6 +8,7 @@ mod s_player_rotation; mod s_swing_arm; pub use s_chat_command::*; +pub use s_chat_message::*; pub use s_confirm_teleport::*; pub use s_player_command::*; pub use s_player_position::*; diff --git a/pumpkin-protocol/src/server/play/s_chat_message.rs b/pumpkin-protocol/src/server/play/s_chat_message.rs new file mode 100644 index 000000000..d2e34de03 --- /dev/null +++ b/pumpkin-protocol/src/server/play/s_chat_message.rs @@ -0,0 +1,29 @@ +use pumpkin_macros::packet; + +use crate::{ + bytebuf::{ByteBuffer, DeserializerError}, + ServerPacket, +}; + +// derive(Deserialize)] +#[packet(0x06)] +pub struct SChatMessage { + pub message: String, + pub timestamp: i64, + pub salt: i64, + pub signature: Option>, + // pub messagee_count: VarInt, + // acknowledged: BitSet, +} + +impl ServerPacket for SChatMessage { + fn read(bytebuf: &mut ByteBuffer) -> Result { + Ok(Self { + message: bytebuf.get_string().unwrap(), + timestamp: bytebuf.get_i64(), + salt: bytebuf.get_i64(), + signature: bytebuf.get_option(|v| v.get_slice().to_vec()), + //messagee_count: bytebuf.get_var_int(), + }) + } +} diff --git a/pumpkin-protocol/src/text.rs b/pumpkin-protocol/src/text.rs index 73ad1b9d9..78ebbfcaa 100644 --- a/pumpkin-protocol/src/text.rs +++ b/pumpkin-protocol/src/text.rs @@ -15,7 +15,7 @@ impl serde::Serialize for TextComponent { where S: serde::Serializer, { - serializer.serialize_bytes(&self.encode()) + serializer.serialize_bytes(self.encode().as_slice()) } } diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index e87619931..c13388a8c 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -84,7 +84,6 @@ pub fn unpack_textures(property: Property, config: &TextureConfig) { // TODO: no unwrap let from64 = general_purpose::STANDARD.decode(property.value).unwrap(); let textures: ProfileTextures = serde_json::from_slice(&from64).unwrap(); - dbg!(&textures); for texture in textures.textures { is_texture_url_valid(Url::parse(&texture.1.url).unwrap(), config); } diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 52d8c072d..aaaf66c59 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -11,7 +11,7 @@ use pumpkin_protocol::{ login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, status::{SPingRequest, SStatusRequest}, }, - ConnectionState, KnownPack, + ConnectionState, KnownPack, RawBytes, }; use pumpkin_registry::Registry; use rsa::Pkcs1v15Encrypt; @@ -64,8 +64,8 @@ impl Client { let public_key_der = &server.public_key_der; let packet = CEncryptionRequest::new( "", - public_key_der, - &verify_token, + RawBytes(public_key_der), + RawBytes(&verify_token), server.base_config.online_mode, // TODO ); self.send_packet(packet); diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index b7ddd230f..319948b9e 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -27,7 +27,7 @@ use pumpkin_protocol::{ handshake::SHandShake, login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, play::{ - SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, + SChatCommand, SChatMessage, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, SSwingArm, }, status::{SPingRequest, SStatusRequest}, @@ -195,10 +195,13 @@ impl Client { SLoginStart::PACKET_ID => { self.handle_login_start(server, SLoginStart::read(bytebuf).unwrap()) } - SEncryptionResponse::PACKET_ID => self.handle_encryption_response( - server, - SEncryptionResponse::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()), SLoginAcknowledged::PACKET_ID => self @@ -261,6 +264,9 @@ impl Client { SSwingArm::PACKET_ID => { self.handle_swing_arm(server, SSwingArm::read(bytebuf).unwrap()) } + SChatMessage::PACKET_ID => { + self.handle_chat_message(server, SChatMessage::read(bytebuf).unwrap()) + } _ => log::error!("Failed to handle player packet id {}", packet.id.0), } } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 34384b9ae..1a07da9d8 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,13 +1,14 @@ use num_traits::FromPrimitive; use pumpkin_protocol::{ client::play::{ - Animation, CEntityAnimation, CHeadRot, CUpdateEntityPos, CUpdateEntityPosRot, - CUpdateEntityRot, + Animation, CEntityAnimation, CHeadRot, CSystemChatMessge, CUpdateEntityPos, + CUpdateEntityPosRot, CUpdateEntityRot, }, server::play::{ - SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, - SPlayerRotation, SSwingArm, + SChatCommand, SChatMessage, SConfirmTeleport, SPlayerCommand, SPlayerPosition, + SPlayerPositionRotation, SPlayerRotation, SSwingArm, }, + text::TextComponent, }; use crate::{ @@ -171,4 +172,45 @@ impl Client { let id = player.entity_id(); server.broadcast_packet_expect(self, CEntityAnimation::new(id.into(), animation as u8)) } + + pub fn handle_chat_message(&mut self, server: &mut Server, chat_message: SChatMessage) { + let message = chat_message.message; + // TODO: filter message & validation + let gameprofile = self.gameprofile.as_ref().unwrap(); + 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)), + false, + ), + ) + /*server.broadcast_packet( + self, + CPlayerChatMessage::new( + gameprofile.id, + 0.into(), + None, + message.clone(), + chat_message.timestamp, + chat_message.salt, + &[], + Some(TextComponent::from(message.clone())), + pumpkin_protocol::VarInt(FilterType::PassThrough as i32), + 0.into(), + TextComponent::from(gameprofile.name.clone()), + None, + ), + ) */ + /* server.broadcast_packet( + self, + CDisguisedChatMessage::new( + TextComponent::from(message.clone()), + VarInt(0), + gameprofile.name.clone().into(), + None, + ), + ) */ + } } diff --git a/pumpkin/src/commands/mod.rs b/pumpkin/src/commands/mod.rs index bc6160f3e..33cb4c819 100644 --- a/pumpkin/src/commands/mod.rs +++ b/pumpkin/src/commands/mod.rs @@ -16,6 +16,7 @@ pub trait Command<'a> { fn on_execute(sender: &mut CommandSender<'a>, command: String); /// Specifies wether the Command Sender has to be a Player + /// TODO: implement fn player_required() -> bool { false } diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 61066f86b..550aa36f5 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -155,6 +155,7 @@ impl Server { 0.into(), false, )); + dbg!("sending abilities"); // player abilities client.send_packet(CPlayerAbilities::new(0x02, 00.2, 0.1));