Add ChatType

This commit is contained in:
Snowiiii 2024-08-12 15:46:24 +02:00
parent 65c2207c0a
commit 5cf19a4e98
16 changed files with 158 additions and 151 deletions

1
Cargo.lock generated
View File

@ -1235,6 +1235,7 @@ dependencies = [
"fastnbt 2.5.0 (git+https://github.com/owengage/fastnbt.git)", "fastnbt 2.5.0 (git+https://github.com/owengage/fastnbt.git)",
"fastsnbt", "fastsnbt",
"pumpkin-protocol", "pumpkin-protocol",
"pumpkin-text",
"serde", "serde",
] ]

View File

@ -1,7 +1,9 @@
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Serialize;
use crate::{bytebuf::ByteBuffer, ClientPacket}; use crate::{bytebuf::ByteBuffer, ClientPacket};
#[derive(Serialize)]
#[packet(0x01)] #[packet(0x01)]
pub struct CPluginMessage<'a> { pub struct CPluginMessage<'a> {
channel: &'a str, channel: &'a str,
@ -13,10 +15,3 @@ impl<'a> CPluginMessage<'a> {
Self { channel, data } Self { channel, data }
} }
} }
impl<'a> ClientPacket for CPluginMessage<'a> {
fn write(&self, bytebuf: &mut ByteBuffer) {
bytebuf.put_string(self.channel);
bytebuf.put_slice(self.data);
}
}

View File

@ -8,24 +8,24 @@ use crate::{RawBytes, VarInt};
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: RawBytes<'a>, public_key: &'a [u8],
verify_token_length: VarInt, verify_token_length: VarInt,
verify_token: RawBytes<'a>, verify_token: &'a [u8],
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: RawBytes<'a>, public_key: &'a [u8],
verify_token: RawBytes<'a>, verify_token: &'a [u8],
should_authenticate: bool, should_authenticate: bool,
) -> Self { ) -> Self {
Self { Self {
server_id, server_id,
public_key_length: public_key.0.len().into(), public_key_length: public_key.len().into(),
public_key, public_key,
verify_token_length: verify_token.0.len().into(), verify_token_length: verify_token.len().into(),
verify_token, verify_token,
should_authenticate, should_authenticate,
} }

View File

@ -3,12 +3,12 @@ use pumpkin_macros::packet;
use pumpkin_text::TextComponent; use pumpkin_text::TextComponent;
use serde::Serialize; use serde::Serialize;
use crate::{BitSet, VarInt}; use crate::{uuid::UUID, BitSet, VarInt};
#[derive(Serialize, Clone)] #[derive(Serialize, Clone)]
#[packet(0x39)] #[packet(0x39)]
pub struct CPlayerChatMessage<'a> { pub struct CPlayerChatMessage<'a> {
sender: uuid::Uuid, sender: UUID,
index: VarInt, index: VarInt,
message_signature: Option<&'a [u8]>, message_signature: Option<&'a [u8]>,
message: String, message: String,
@ -29,7 +29,7 @@ pub struct CPlayerChatMessage<'a> {
impl<'a> CPlayerChatMessage<'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,
index: VarInt, index: VarInt,
message_signature: Option<&'a [u8]>, message_signature: Option<&'a [u8]>,
message: String, message: String,

View File

@ -1,12 +1,13 @@
use pumpkin_macros::packet; use pumpkin_macros::packet;
use serde::Serialize;
use crate::{ClientPacket, VarInt}; use crate::{uuid::UUID, VarInt};
#[derive(Clone)] #[derive(Serialize, Clone)]
#[packet(0x01)] #[packet(0x01)]
pub struct CSpawnEntity { pub struct CSpawnEntity {
entity_id: VarInt, entity_id: VarInt,
entity_uuid: uuid::Uuid, entity_uuid: UUID,
typ: VarInt, typ: VarInt,
x: f64, x: f64,
y: f64, y: f64,
@ -24,7 +25,7 @@ impl CSpawnEntity {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
entity_id: VarInt, entity_id: VarInt,
entity_uuid: uuid::Uuid, entity_uuid: UUID,
typ: VarInt, typ: VarInt,
x: f64, x: f64,
y: f64, y: f64,
@ -54,21 +55,3 @@ impl CSpawnEntity {
} }
} }
} }
impl ClientPacket for CSpawnEntity {
fn write(&self, bytebuf: &mut crate::bytebuf::ByteBuffer) {
bytebuf.put_var_int(&self.entity_id);
bytebuf.put_uuid(self.entity_uuid);
bytebuf.put_var_int(&self.typ);
bytebuf.put_f64(self.x);
bytebuf.put_f64(self.y);
bytebuf.put_f64(self.z);
bytebuf.put_u8(self.pitch);
bytebuf.put_u8(self.yaw);
bytebuf.put_u8(self.head_yaw);
bytebuf.put_var_int(&self.data);
bytebuf.put_i16(self.velocity_x);
bytebuf.put_i16(self.velocity_y);
bytebuf.put_i16(self.velocity_z);
}
}

View File

@ -91,16 +91,6 @@ 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,5 +1,6 @@
use serde::Serialize; use serde::Serialize;
#[derive(Clone)]
pub struct UUID(pub uuid::Uuid); pub struct UUID(pub uuid::Uuid);
impl Serialize for UUID { impl Serialize for UUID {

View File

@ -5,6 +5,8 @@ edition.workspace = true
[dependencies] [dependencies]
pumpkin-protocol = { path = "../pumpkin-protocol"} pumpkin-protocol = { path = "../pumpkin-protocol"}
pumpkin-text = { path = "../pumpkin-text"}
# nbt # nbt
fastnbt = { git = "https://github.com/owengage/fastnbt.git" } fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
fastsnbt = "0.2" fastsnbt = "0.2"

View File

@ -1,3 +1,4 @@
use pumpkin_text::style::Style;
use serde::Serialize; use serde::Serialize;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
@ -9,9 +10,8 @@ pub struct ChatType {
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct Decoration { pub struct Decoration {
translation_key: String, translation_key: String,
style: u8, #[serde(default, skip_serializing_if = "Option::is_none")]
// TODO style: Option<Style>,
// style: Option<Styles>,
parameters: Vec<String>, parameters: Vec<String>,
} }
@ -19,12 +19,12 @@ impl Default for ChatType {
fn default() -> Self { fn default() -> Self {
Self { Self {
chat: Decoration { chat: Decoration {
style: 0, style: None,
parameters: vec!["sender".into(), "content".into()], parameters: vec!["sender".into(), "content".into()],
translation_key: "chat.type.text".into(), translation_key: "chat.type.text".into(),
}, },
narration: Decoration { narration: Decoration {
style: 0, style: None,
parameters: vec!["sender".into(), "content".into()], parameters: vec!["sender".into(), "content".into()],
translation_key: "chat.type.text.narrate".into(), translation_key: "chat.type.text.narrate".into(),
}, },

View File

@ -52,7 +52,7 @@ impl Registry {
}], }],
}; };
let _chat_types = Registry { let chat_types = Registry {
registry_id: "minecraft:chat_type".to_string(), registry_id: "minecraft:chat_type".to_string(),
registry_entries: vec![RegistryEntry { registry_entries: vec![RegistryEntry {
entry_id: "minecraft:chat", entry_id: "minecraft:chat",
@ -79,7 +79,7 @@ impl Registry {
biomes, biomes,
wolf_variants, wolf_variants,
paintings, paintings,
// chat_types, chat_types,
] ]
} }
} }

View File

@ -5,10 +5,12 @@ use color::Color;
use fastnbt::SerOpts; use fastnbt::SerOpts;
use hover::HoverEvent; use hover::HoverEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use style::Style;
pub mod click; pub mod click;
pub mod color; pub mod color;
pub mod hover; pub mod hover;
pub mod style;
#[derive(Clone, Default, Debug, Serialize, Deserialize)] #[derive(Clone, Default, Debug, Serialize, Deserialize)]
#[serde(transparent)] #[serde(transparent)]
@ -22,39 +24,10 @@ pub struct TextComponent {
/// The actual text /// The actual text
#[serde(flatten)] #[serde(flatten)]
pub content: TextContent, pub content: TextContent,
#[serde(default, skip_serializing_if = "Option::is_none")] /// Style of the text. Bold, Italic, unterline, Color...
/// Also has `ClickEvent
#[serde(flatten)] #[serde(flatten)]
/// Changes the color to render the content pub style: Style,
pub color: Option<Color>,
/// Whether to render the content in bold.
/// Keep in mind that booleans are representet as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bold: Option<u8>,
/// Whether to render the content in italic.
/// Keep in mind that booleans are representet as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub italic: Option<u8>,
/// Whether to render the content in underlined.
/// Keep in mind that booleans are representet as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub underlined: Option<u8>,
/// Whether to render the content in strikethrough.
/// Keep in mind that booleans are representet as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub strikethrough: Option<u8>,
/// Whether to render the content in obfuscated.
/// Keep in mind that booleans are representet as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub obfuscated: Option<u8>,
/// When the text is shift-clicked by a player, this string is inserted in their chat input. It does not overwrite any existing text the player was writing. This only works in chat messages
#[serde(default, skip_serializing_if = "Option::is_none")]
pub insertion: Option<String>,
/// Allows for events to occur when the player clicks on text. Only work in chat.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub click_event: Option<ClickEvent>,
/// Allows for a tooltip to be displayed when the player hovers their mouse over text.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hover_event: Option<HoverEvent>,
} }
impl serde::Serialize for TextComponent { impl serde::Serialize for TextComponent {
@ -68,60 +41,60 @@ impl serde::Serialize for TextComponent {
impl TextComponent { impl TextComponent {
pub fn color(mut self, color: Color) -> Self { pub fn color(mut self, color: Color) -> Self {
self.color = Some(color); self.style.color = Some(color);
self self
} }
pub fn color_named(mut self, color: color::NamedColor) -> Self { pub fn color_named(mut self, color: color::NamedColor) -> Self {
self.color = Some(Color::Named(color)); self.style.color = Some(Color::Named(color));
self self
} }
/// Makes the text bold /// Makes the text bold
pub fn bold(mut self) -> Self { pub fn bold(mut self) -> Self {
self.bold = Some(1); self.style.bold = Some(1);
self self
} }
/// Makes the text italic /// Makes the text italic
pub fn italic(mut self) -> Self { pub fn italic(mut self) -> Self {
self.italic = Some(1); self.style.italic = Some(1);
self self
} }
/// Makes the text underlined /// Makes the text underlined
pub fn underlined(mut self) -> Self { pub fn underlined(mut self) -> Self {
self.underlined = Some(1); self.style.underlined = Some(1);
self self
} }
/// Makes the text strikethrough /// Makes the text strikethrough
pub fn strikethrough(mut self) -> Self { pub fn strikethrough(mut self) -> Self {
self.strikethrough = Some(1); self.style.strikethrough = Some(1);
self self
} }
/// Makes the text obfuscated /// Makes the text obfuscated
pub fn obfuscated(mut self) -> Self { pub fn obfuscated(mut self) -> Self {
self.obfuscated = Some(1); self.style.obfuscated = Some(1);
self self
} }
/// When the text is shift-clicked by a player, this string is inserted in their chat input. It does not overwrite any existing text the player was writing. This only works in chat messages /// When the text is shift-clicked by a player, this string is inserted in their chat input. It does not overwrite any existing text the player was writing. This only works in chat messages
pub fn insertion(mut self, text: String) -> Self { pub fn insertion(mut self, text: String) -> Self {
self.insertion = Some(text); self.style.insertion = Some(text);
self self
} }
/// Allows for events to occur when the player clicks on text. Only work in chat. /// Allows for events to occur when the player clicks on text. Only work in chat.
pub fn click_event(mut self, event: ClickEvent) -> Self { pub fn click_event(mut self, event: ClickEvent) -> Self {
self.click_event = Some(event); self.style.click_event = Some(event);
self self
} }
/// Allows for a tooltip to be displayed when the player hovers their mouse over text. /// Allows for a tooltip to be displayed when the player hovers their mouse over text.
pub fn hover_event(mut self, event: HoverEvent) -> Self { pub fn hover_event(mut self, event: HoverEvent) -> Self {
self.hover_event = Some(event); self.style.hover_event = Some(event);
self self
} }
@ -132,36 +105,12 @@ impl TextComponent {
struct TempStruct<'a> { struct TempStruct<'a> {
#[serde(flatten)] #[serde(flatten)]
text: &'a TextContent, text: &'a TextContent,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(flatten)]
pub color: &'a Option<Color>, pub style: &'a Style,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bold: &'a Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub italic: &'a Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub underlined: &'a Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub strikethrough: &'a Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub obfuscated: &'a Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub insertion: &'a Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub click_event: &'a Option<ClickEvent>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hover_event: &'a Option<HoverEvent>,
} }
let astruct = TempStruct { let astruct = TempStruct {
text: &self.content, text: &self.content,
color: &self.color, style: &self.style,
bold: &self.bold,
italic: &self.italic,
underlined: &self.underlined,
strikethrough: &self.strikethrough,
obfuscated: &self.obfuscated,
insertion: &self.insertion,
click_event: &self.click_event,
hover_event: &self.hover_event,
}; };
// dbg!(&serde_json::to_string(&astruct)); // dbg!(&serde_json::to_string(&astruct));
@ -173,15 +122,7 @@ impl From<String> for TextComponent {
fn from(value: String) -> Self { fn from(value: String) -> Self {
Self { Self {
content: TextContent::Text { text: value }, content: TextContent::Text { text: value },
color: None, style: Style::default(),
bold: None,
italic: None,
underlined: None,
strikethrough: None,
obfuscated: None,
insertion: None,
click_event: None,
hover_event: None,
} }
} }
} }
@ -192,15 +133,7 @@ impl From<&str> for TextComponent {
content: TextContent::Text { content: TextContent::Text {
text: value.to_string(), text: value.to_string(),
}, },
color: None, style: Style::default(),
bold: None,
italic: None,
underlined: None,
strikethrough: None,
obfuscated: None,
insertion: None,
click_event: None,
hover_event: None,
} }
} }
} }

100
pumpkin-text/src/style.rs Normal file
View File

@ -0,0 +1,100 @@
use serde::{Deserialize, Serialize};
use crate::{
click::ClickEvent,
color::{self, Color},
hover::HoverEvent,
};
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct Style {
/// Changes the color to render the content
pub color: Option<Color>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bold: Option<u8>,
/// Whether to render the content in italic.
/// Keep in mind that booleans are representet as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub italic: Option<u8>,
/// Whether to render the content in underlined.
/// Keep in mind that booleans are representet as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub underlined: Option<u8>,
/// Whether to render the content in strikethrough.
/// Keep in mind that booleans are representet as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub strikethrough: Option<u8>,
/// Whether to render the content in obfuscated.
/// Keep in mind that booleans are representet as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub obfuscated: Option<u8>,
/// When the text is shift-clicked by a player, this string is inserted in their chat input. It does not overwrite any existing text the player was writing. This only works in chat messages
#[serde(default, skip_serializing_if = "Option::is_none")]
pub insertion: Option<String>,
/// Allows for events to occur when the player clicks on text. Only work in chat.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub click_event: Option<ClickEvent>,
/// Allows for a tooltip to be displayed when the player hovers their mouse over text.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hover_event: Option<HoverEvent>,
}
impl Style {
pub fn color(mut self, color: Color) -> Self {
self.color = Some(color);
self
}
pub fn color_named(mut self, color: color::NamedColor) -> Self {
self.color = Some(Color::Named(color));
self
}
/// Makes the text bold
pub fn bold(mut self) -> Self {
self.bold = Some(1);
self
}
/// Makes the text italic
pub fn italic(mut self) -> Self {
self.italic = Some(1);
self
}
/// Makes the text underlined
pub fn underlined(mut self) -> Self {
self.underlined = Some(1);
self
}
/// Makes the text strikethrough
pub fn strikethrough(mut self) -> Self {
self.strikethrough = Some(1);
self
}
/// Makes the text obfuscated
pub fn obfuscated(mut self) -> Self {
self.obfuscated = Some(1);
self
}
/// When the text is shift-clicked by a player, this string is inserted in their chat input. It does not overwrite any existing text the player was writing. This only works in chat messages
pub fn insertion(mut self, text: String) -> Self {
self.insertion = Some(text);
self
}
/// Allows for events to occur when the player clicks on text. Only work in chat.
pub fn click_event(mut self, event: ClickEvent) -> Self {
self.click_event = Some(event);
self
}
/// Allows for a tooltip to be displayed when the player hovers their mouse over text.
pub fn hover_event(mut self, event: HoverEvent) -> Self {
self.hover_event = Some(event);
self
}
}

View File

@ -66,8 +66,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(
"", "",
RawBytes(public_key_der), public_key_der,
RawBytes(&verify_token), &verify_token,
server.base_config.online_mode, // TODO server.base_config.online_mode, // TODO
); );
self.send_packet(packet); self.send_packet(packet);

View File

@ -2,8 +2,8 @@ use num_traits::FromPrimitive;
use pumpkin_inventory::WindowType; use pumpkin_inventory::WindowType;
use pumpkin_protocol::{ use pumpkin_protocol::{
client::play::{ client::play::{
Animation, CEntityAnimation, CHeadRot, COpenScreen, CPlayerChatMessage, CUpdateEntityPos, Animation, CEntityAnimation, CHeadRot, COpenScreen, CUpdateEntityPos, CUpdateEntityPosRot,
CUpdateEntityPosRot, CUpdateEntityRot, FilterType, CUpdateEntityRot,
}, },
server::play::{ server::play::{
SChatCommand, SChatMessage, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SChatCommand, SChatMessage, SConfirmTeleport, SPlayerCommand, SPlayerPosition,
@ -210,10 +210,10 @@ impl Client {
// false, // false,
// ), // ),
// ); // );
server.broadcast_packet( /* server.broadcast_packet(
self, self,
CPlayerChatMessage::new( CPlayerChatMessage::new(
gameprofile.id, pumpkin_protocol::uuid::UUID(gameprofile.id),
0.into(), 0.into(),
None, None,
message.clone(), message.clone(),
@ -227,6 +227,8 @@ impl Client {
None, None,
), ),
) )
*/
/* server.broadcast_packet( /* server.broadcast_packet(
self, self,
CDisguisedChatMessage::new( CDisguisedChatMessage::new(

View File

@ -15,6 +15,6 @@ impl<'a> Command<'a> for PumpkinCommand {
fn on_execute(sender: &mut super::CommandSender<'a>, _command: String) { fn on_execute(sender: &mut super::CommandSender<'a>, _command: String) {
let version = env!("CARGO_PKG_VERSION"); let version = env!("CARGO_PKG_VERSION");
let description = env!("CARGO_PKG_DESCRIPTION"); let description = env!("CARGO_PKG_DESCRIPTION");
sender.send_message(TextComponent::from(format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})")).color_named(NamedColor::Green)) sender.send_message(TextComponent::from(format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})")).color_named(NamedColor::Green).bold())
} }
} }

View File

@ -233,7 +233,7 @@ impl Server {
// TODO: add velo // TODO: add velo
CSpawnEntity::new( CSpawnEntity::new(
entity_id.into(), entity_id.into(),
gameprofile.id, UUID(gameprofile.id),
EntityType::Player.to_i32().unwrap().into(), EntityType::Player.to_i32().unwrap().into(),
x, x,
y, y,
@ -256,7 +256,7 @@ impl Server {
let gameprofile = existing_client.gameprofile.as_ref().unwrap(); let gameprofile = existing_client.gameprofile.as_ref().unwrap();
client.send_packet(CSpawnEntity::new( client.send_packet(CSpawnEntity::new(
player.entity_id().into(), player.entity_id().into(),
gameprofile.id, UUID(gameprofile.id),
EntityType::Player.to_i32().unwrap().into(), EntityType::Player.to_i32().unwrap().into(),
entity.x, entity.x,
entity.y, entity.y,