Add Chat support

This commit is contained in:
Snowiiii 2024-08-08 11:27:09 +02:00
parent 1076cc69f0
commit fe8cd334e1
18 changed files with 171 additions and 48 deletions

View File

@ -57,7 +57,7 @@ Pumpkin is currently under heavy development.
- [ ] Player Inventory - [ ] Player Inventory
- [ ] Player Attack - [ ] Player Attack
- Server - Server
- [ ] Chat - [x] Chat
- [x] Commands - [x] Commands
Check out our [Github Project](https://github.com/users/Snowiiii/projects/12/views/1) to see current progress Check out our [Github Project](https://github.com/users/Snowiiii/projects/12/views/1) to see current progress

View File

@ -1,10 +1,11 @@
use std::fmt::Display; use std::fmt::Display;
use serde::{ser, Serialize}; use serde::{
ser::{self},
Serialize,
};
use thiserror::Error; use thiserror::Error;
use crate::VarInt;
use super::ByteBuffer; use super::ByteBuffer;
pub struct Serializer { pub struct Serializer {
@ -126,10 +127,9 @@ impl<'a> ser::Serializer for &'a mut Serializer {
self.output.put_bool(false); self.output.put_bool(false);
Ok(()) Ok(())
} }
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> { fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
if let Some(len) = len { // here is where all arrays/list getting written, usally we prefix the length of every length with an var int. The problem is
self.output.put_var_int(&VarInt(len as i32)); // that byte arrays also getting thrown in here, and we don't want to prefix them
}
Ok(self) Ok(self)
} }
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error> fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
@ -137,7 +137,6 @@ impl<'a> ser::Serializer for &'a mut Serializer {
T: ?Sized + Serialize, T: ?Sized + Serialize,
{ {
self.output.put_bool(true); self.output.put_bool(true);
dbg!("aa");
value.serialize(self) value.serialize(self)
} }
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> { fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
@ -288,7 +287,7 @@ impl<'a> ser::SerializeTupleVariant for &'a mut Serializer {
} }
fn end(self) -> Result<(), Self::Error> { fn end(self) -> Result<(), Self::Error> {
todo!() Ok(())
} }
} }

View File

@ -1,42 +1,33 @@
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Serialize;
use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt}; use crate::{RawBytes, VarInt};
#[derive(Serialize)]
#[packet(0x01)] #[packet(0x01)]
pub struct CEncryptionRequest<'a> { pub struct CEncryptionRequest<'a> {
server_id: &'a str, // 20 server_id: &'a str, // 20
public_key_length: VarInt, public_key_length: VarInt,
public_key: &'a [u8], public_key: RawBytes<'a>,
verify_token_length: VarInt, verify_token_length: VarInt,
verify_token: &'a [u8], verify_token: RawBytes<'a>,
should_authenticate: bool, should_authenticate: bool,
} }
impl<'a> CEncryptionRequest<'a> { impl<'a> CEncryptionRequest<'a> {
pub fn new( pub fn new(
server_id: &'a str, server_id: &'a str,
public_key: &'a [u8], public_key: RawBytes<'a>,
verify_token: &'a [u8], verify_token: RawBytes<'a>,
should_authenticate: bool, should_authenticate: bool,
) -> Self { ) -> Self {
Self { Self {
server_id, server_id,
public_key_length: public_key.len().into(), public_key_length: public_key.0.len().into(),
public_key, public_key,
verify_token_length: verify_token.len().into(), verify_token_length: verify_token.0.len().into(),
verify_token, verify_token,
should_authenticate, 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);
}
}

View File

@ -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<TextComponent>,
}
impl CDisguisedChatMessage {
pub fn new(
message: TextComponent,
chat_type: VarInt,
sender_name: TextComponent,
target_name: Option<TextComponent>,
) -> 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()));
}
}

View File

@ -8,6 +8,7 @@ use crate::VarInt;
pub struct CLogin { pub struct CLogin {
entity_id: i32, entity_id: i32,
is_hardcore: bool, is_hardcore: bool,
dimension_count: VarInt,
dimension_names: Vec<String>, dimension_names: Vec<String>,
max_players: VarInt, max_players: VarInt,
view_distance: VarInt, view_distance: VarInt,
@ -53,6 +54,7 @@ impl CLogin {
Self { Self {
entity_id, entity_id,
is_hardcore, is_hardcore,
dimension_count: VarInt(dimension_names.len() as i32),
dimension_names, dimension_names,
max_players, max_players,
view_distance, view_distance,

View File

@ -1,10 +1,12 @@
use num_derive::FromPrimitive; use num_derive::{FromPrimitive, ToPrimitive};
use pumpkin_macros::packet;
use serde::Serialize; use serde::Serialize;
use crate::{text::TextComponent, VarInt}; use crate::{text::TextComponent, VarInt};
#[derive(Serialize)] #[derive(Serialize, Clone)]
pub struct PlayerChatMessage<'a> { #[packet(0x39)]
pub struct CPlayerChatMessage<'a> {
sender: uuid::Uuid, sender: uuid::Uuid,
index: VarInt, index: VarInt,
message_signature: Option<&'a [u8]>, message_signature: Option<&'a [u8]>,
@ -20,7 +22,7 @@ pub struct PlayerChatMessage<'a> {
target_name: Option<TextComponent>, target_name: Option<TextComponent>,
} }
impl<'a> PlayerChatMessage<'a> { impl<'a> CPlayerChatMessage<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
sender: uuid::Uuid, sender: uuid::Uuid,
@ -59,7 +61,7 @@ pub struct PreviousMessage<'a> {
signature: Option<&'a [u8]>, signature: Option<&'a [u8]>,
} }
#[derive(FromPrimitive)] #[derive(FromPrimitive, ToPrimitive)]
pub enum FilterType { pub enum FilterType {
/// Message is not filtered at all /// Message is not filtered at all
PassThrough, PassThrough,

View File

@ -3,7 +3,7 @@ use serde::Serialize;
use crate::text::TextComponent; use crate::text::TextComponent;
#[derive(Serialize)] #[derive(Serialize, Clone)]
#[packet(0x6C)] #[packet(0x6C)]
pub struct CSystemChatMessge { pub struct CSystemChatMessge {
content: TextComponent, content: TextComponent,

View File

@ -1,5 +1,6 @@
mod c_change_difficulty; mod c_change_difficulty;
mod c_chunk_data_update_light; mod c_chunk_data_update_light;
mod c_disguised_chat_message;
mod c_entity_animation; mod c_entity_animation;
mod c_entity_metadata; mod c_entity_metadata;
mod c_game_event; mod c_game_event;
@ -21,6 +22,7 @@ mod player_action;
pub use c_change_difficulty::*; pub use c_change_difficulty::*;
pub use c_chunk_data_update_light::*; pub use c_chunk_data_update_light::*;
pub use c_disguised_chat_message::*;
pub use c_entity_animation::*; pub use c_entity_animation::*;
pub use c_entity_metadata::*; pub use c_entity_metadata::*;
pub use c_game_event::*; pub use c_game_event::*;

View File

@ -91,6 +91,16 @@ impl VarInt {
} }
} }
pub struct RawBytes<'a>(pub &'a [u8]);
impl<'a> Serialize for RawBytes<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bytes(self.0)
}
}
impl From<i32> for VarInt { impl From<i32> for VarInt {
fn from(value: i32) -> Self { fn from(value: i32) -> Self {
VarInt(value) VarInt(value)

View File

@ -1,4 +1,5 @@
mod s_chat_command; mod s_chat_command;
mod s_chat_message;
mod s_confirm_teleport; mod s_confirm_teleport;
mod s_player_command; mod s_player_command;
mod s_player_position; mod s_player_position;
@ -7,6 +8,7 @@ mod s_player_rotation;
mod s_swing_arm; mod s_swing_arm;
pub use s_chat_command::*; pub use s_chat_command::*;
pub use s_chat_message::*;
pub use s_confirm_teleport::*; pub use s_confirm_teleport::*;
pub use s_player_command::*; pub use s_player_command::*;
pub use s_player_position::*; pub use s_player_position::*;

View File

@ -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<Vec<u8>>,
// pub messagee_count: VarInt,
// acknowledged: BitSet,
}
impl ServerPacket for SChatMessage {
fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> {
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(),
})
}
}

View File

@ -15,7 +15,7 @@ impl serde::Serialize for TextComponent {
where where
S: serde::Serializer, S: serde::Serializer,
{ {
serializer.serialize_bytes(&self.encode()) serializer.serialize_bytes(self.encode().as_slice())
} }
} }

View File

@ -84,7 +84,6 @@ pub fn unpack_textures(property: Property, config: &TextureConfig) {
// TODO: no unwrap // TODO: no unwrap
let from64 = general_purpose::STANDARD.decode(property.value).unwrap(); let from64 = general_purpose::STANDARD.decode(property.value).unwrap();
let textures: ProfileTextures = serde_json::from_slice(&from64).unwrap(); let textures: ProfileTextures = serde_json::from_slice(&from64).unwrap();
dbg!(&textures);
for texture in textures.textures { for texture in textures.textures {
is_texture_url_valid(Url::parse(&texture.1.url).unwrap(), config); is_texture_url_valid(Url::parse(&texture.1.url).unwrap(), config);
} }

View File

@ -11,7 +11,7 @@ use pumpkin_protocol::{
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
status::{SPingRequest, SStatusRequest}, status::{SPingRequest, SStatusRequest},
}, },
ConnectionState, KnownPack, ConnectionState, KnownPack, RawBytes,
}; };
use pumpkin_registry::Registry; use pumpkin_registry::Registry;
use rsa::Pkcs1v15Encrypt; use rsa::Pkcs1v15Encrypt;
@ -64,8 +64,8 @@ impl Client {
let public_key_der = &server.public_key_der; let public_key_der = &server.public_key_der;
let packet = CEncryptionRequest::new( let packet = CEncryptionRequest::new(
"", "",
public_key_der, RawBytes(public_key_der),
&verify_token, RawBytes(&verify_token),
server.base_config.online_mode, // TODO server.base_config.online_mode, // TODO
); );
self.send_packet(packet); self.send_packet(packet);

View File

@ -27,7 +27,7 @@ use pumpkin_protocol::{
handshake::SHandShake, handshake::SHandShake,
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
play::{ play::{
SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SChatCommand, SChatMessage, SConfirmTeleport, SPlayerCommand, SPlayerPosition,
SPlayerPositionRotation, SPlayerRotation, SSwingArm, SPlayerPositionRotation, SPlayerRotation, SSwingArm,
}, },
status::{SPingRequest, SStatusRequest}, status::{SPingRequest, SStatusRequest},
@ -195,10 +195,13 @@ impl Client {
SLoginStart::PACKET_ID => { SLoginStart::PACKET_ID => {
self.handle_login_start(server, SLoginStart::read(bytebuf).unwrap()) self.handle_login_start(server, SLoginStart::read(bytebuf).unwrap())
} }
SEncryptionResponse::PACKET_ID => self.handle_encryption_response( SEncryptionResponse::PACKET_ID => {
self.handle_encryption_response(
server, server,
SEncryptionResponse::read(bytebuf).unwrap(), SEncryptionResponse::read(bytebuf).unwrap(),
).await, )
.await
}
SLoginPluginResponse::PACKET_ID => self SLoginPluginResponse::PACKET_ID => self
.handle_plugin_response(server, SLoginPluginResponse::read(bytebuf).unwrap()), .handle_plugin_response(server, SLoginPluginResponse::read(bytebuf).unwrap()),
SLoginAcknowledged::PACKET_ID => self SLoginAcknowledged::PACKET_ID => self
@ -261,6 +264,9 @@ impl Client {
SSwingArm::PACKET_ID => { SSwingArm::PACKET_ID => {
self.handle_swing_arm(server, SSwingArm::read(bytebuf).unwrap()) 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), _ => log::error!("Failed to handle player packet id {}", packet.id.0),
} }
} }

View File

@ -1,13 +1,14 @@
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use pumpkin_protocol::{ use pumpkin_protocol::{
client::play::{ client::play::{
Animation, CEntityAnimation, CHeadRot, CUpdateEntityPos, CUpdateEntityPosRot, Animation, CEntityAnimation, CHeadRot, CSystemChatMessge, CUpdateEntityPos,
CUpdateEntityRot, CUpdateEntityPosRot, CUpdateEntityRot,
}, },
server::play::{ server::play::{
SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, SChatCommand, SChatMessage, SConfirmTeleport, SPlayerCommand, SPlayerPosition,
SPlayerRotation, SSwingArm, SPlayerPositionRotation, SPlayerRotation, SSwingArm,
}, },
text::TextComponent,
}; };
use crate::{ use crate::{
@ -171,4 +172,45 @@ impl Client {
let id = player.entity_id(); let id = player.entity_id();
server.broadcast_packet_expect(self, CEntityAnimation::new(id.into(), animation as u8)) 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,
),
) */
}
} }

View File

@ -16,6 +16,7 @@ pub trait Command<'a> {
fn on_execute(sender: &mut CommandSender<'a>, command: String); fn on_execute(sender: &mut CommandSender<'a>, command: String);
/// Specifies wether the Command Sender has to be a Player /// Specifies wether the Command Sender has to be a Player
/// TODO: implement
fn player_required() -> bool { fn player_required() -> bool {
false false
} }

View File

@ -155,6 +155,7 @@ impl Server {
0.into(), 0.into(),
false, false,
)); ));
dbg!("sending abilities");
// player abilities // player abilities
client.send_packet(CPlayerAbilities::new(0x02, 00.2, 0.1)); client.send_packet(CPlayerAbilities::new(0x02, 00.2, 0.1));