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)",
"fastsnbt",
"pumpkin-protocol",
"pumpkin-text",
"serde",
]

View File

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

View File

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

View File

@@ -1,12 +1,13 @@
use pumpkin_macros::packet;
use serde::Serialize;
use crate::{ClientPacket, VarInt};
use crate::{uuid::UUID, VarInt};
#[derive(Clone)]
#[derive(Serialize, Clone)]
#[packet(0x01)]
pub struct CSpawnEntity {
entity_id: VarInt,
entity_uuid: uuid::Uuid,
entity_uuid: UUID,
typ: VarInt,
x: f64,
y: f64,
@@ -24,7 +25,7 @@ impl CSpawnEntity {
#[allow(clippy::too_many_arguments)]
pub fn new(
entity_id: VarInt,
entity_uuid: uuid::Uuid,
entity_uuid: UUID,
typ: VarInt,
x: 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 {
fn from(value: i32) -> Self {
VarInt(value)

View File

@@ -1,5 +1,6 @@
use serde::Serialize;
#[derive(Clone)]
pub struct UUID(pub uuid::Uuid);
impl Serialize for UUID {

View File

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

View File

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

View File

@@ -5,10 +5,12 @@ use color::Color;
use fastnbt::SerOpts;
use hover::HoverEvent;
use serde::{Deserialize, Serialize};
use style::Style;
pub mod click;
pub mod color;
pub mod hover;
pub mod style;
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
#[serde(transparent)]
@@ -22,39 +24,10 @@ pub struct TextComponent {
/// The actual text
#[serde(flatten)]
pub content: TextContent,
#[serde(default, skip_serializing_if = "Option::is_none")]
/// Style of the text. Bold, Italic, unterline, Color...
/// Also has `ClickEvent
#[serde(flatten)]
/// Changes the color to render the content
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>,
pub style: Style,
}
impl serde::Serialize for TextComponent {
@@ -68,60 +41,60 @@ impl serde::Serialize for TextComponent {
impl TextComponent {
pub fn color(mut self, color: Color) -> Self {
self.color = Some(color);
self.style.color = Some(color);
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
}
/// Makes the text bold
pub fn bold(mut self) -> Self {
self.bold = Some(1);
self.style.bold = Some(1);
self
}
/// Makes the text italic
pub fn italic(mut self) -> Self {
self.italic = Some(1);
self.style.italic = Some(1);
self
}
/// Makes the text underlined
pub fn underlined(mut self) -> Self {
self.underlined = Some(1);
self.style.underlined = Some(1);
self
}
/// Makes the text strikethrough
pub fn strikethrough(mut self) -> Self {
self.strikethrough = Some(1);
self.style.strikethrough = Some(1);
self
}
/// Makes the text obfuscated
pub fn obfuscated(mut self) -> Self {
self.obfuscated = Some(1);
self.style.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.style.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.style.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.style.hover_event = Some(event);
self
}
@@ -132,36 +105,12 @@ impl TextComponent {
struct TempStruct<'a> {
#[serde(flatten)]
text: &'a TextContent,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub color: &'a Option<Color>,
#[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>,
#[serde(flatten)]
pub style: &'a Style,
}
let astruct = TempStruct {
text: &self.content,
color: &self.color,
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,
style: &self.style,
};
// dbg!(&serde_json::to_string(&astruct));
@@ -173,15 +122,7 @@ impl From<String> for TextComponent {
fn from(value: String) -> Self {
Self {
content: TextContent::Text { text: value },
color: None,
bold: None,
italic: None,
underlined: None,
strikethrough: None,
obfuscated: None,
insertion: None,
click_event: None,
hover_event: None,
style: Style::default(),
}
}
}
@@ -192,15 +133,7 @@ impl From<&str> for TextComponent {
content: TextContent::Text {
text: value.to_string(),
},
color: None,
bold: None,
italic: None,
underlined: None,
strikethrough: None,
obfuscated: None,
insertion: None,
click_event: None,
hover_event: None,
style: Style::default(),
}
}
}

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 packet = CEncryptionRequest::new(
"",
RawBytes(public_key_der),
RawBytes(&verify_token),
public_key_der,
&verify_token,
server.base_config.online_mode, // TODO
);
self.send_packet(packet);

View File

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

View File

@@ -15,6 +15,6 @@ impl<'a> Command<'a> for PumpkinCommand {
fn on_execute(sender: &mut super::CommandSender<'a>, _command: String) {
let version = env!("CARGO_PKG_VERSION");
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
CSpawnEntity::new(
entity_id.into(),
gameprofile.id,
UUID(gameprofile.id),
EntityType::Player.to_i32().unwrap().into(),
x,
y,
@@ -256,7 +256,7 @@ impl Server {
let gameprofile = existing_client.gameprofile.as_ref().unwrap();
client.send_packet(CSpawnEntity::new(
player.entity_id().into(),
gameprofile.id,
UUID(gameprofile.id),
EntityType::Player.to_i32().unwrap().into(),
entity.x,
entity.y,