azalea_protocol/packets/game/
c_player_chat.rs

1use std::io::{Cursor, Write};
2
3use azalea_buf::{AzBuf, AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
4use azalea_chat::{
5    FormattedText,
6    translatable_component::{StringOrComponent, TranslatableComponent},
7};
8use azalea_core::bitset::BitSet;
9use azalea_crypto::MessageSignature;
10use azalea_protocol_macros::ClientboundGamePacket;
11use azalea_registry::Holder;
12use simdnbt::owned::NbtCompound;
13use uuid::Uuid;
14
15#[derive(Clone, Debug, AzBuf, ClientboundGamePacket, PartialEq)]
16pub struct ClientboundPlayerChat {
17    #[var]
18    pub global_index: u32,
19    pub sender: Uuid,
20    #[var]
21    pub index: u32,
22    pub signature: Option<MessageSignature>,
23    pub body: PackedSignedMessageBody,
24    pub unsigned_content: Option<FormattedText>,
25    pub filter_mask: FilterMask,
26    pub chat_type: ChatTypeBound,
27}
28
29#[derive(Clone, Debug, PartialEq, AzBuf)]
30pub struct PackedSignedMessageBody {
31    // the error is here, for some reason it skipped a byte earlier and here
32    // it's reading `0` when it should be `11`
33    pub content: String,
34    pub timestamp: u64,
35    pub salt: u64,
36    pub last_seen: PackedLastSeenMessages,
37}
38
39#[derive(Clone, Debug, PartialEq, AzBuf)]
40pub struct PackedLastSeenMessages {
41    pub entries: Vec<PackedMessageSignature>,
42}
43
44/// Messages can be deleted by either their signature or message id.
45#[derive(Clone, Debug, PartialEq)]
46pub enum PackedMessageSignature {
47    Signature(Box<MessageSignature>),
48    Id(u32),
49}
50
51#[derive(Clone, Debug, PartialEq, AzBuf)]
52pub enum FilterMask {
53    PassThrough,
54    FullyFiltered,
55    PartiallyFiltered(BitSet),
56}
57
58#[derive(Clone, Debug, PartialEq, AzBuf)]
59pub struct ChatTypeBound {
60    pub chat_type: Holder<azalea_registry::ChatType, DirectChatType>,
61    pub name: FormattedText,
62    pub target_name: Option<FormattedText>,
63}
64
65#[derive(Clone, Debug, PartialEq, AzBuf)]
66pub struct DirectChatType {
67    pub chat: ChatTypeDecoration,
68    pub narration: ChatTypeDecoration,
69}
70#[derive(Clone, Debug, PartialEq, AzBuf)]
71pub struct ChatTypeDecoration {
72    pub translation_key: String,
73    pub parameters: Vec<ChatTypeDecorationParameter>,
74    pub style: NbtCompound,
75}
76
77#[derive(Clone, Copy, Debug, PartialEq, AzBuf)]
78pub enum ChatTypeDecorationParameter {
79    Sender = 0,
80    Target = 1,
81    Content = 2,
82}
83
84// must be in Client
85#[derive(Clone, Debug, PartialEq)]
86pub struct MessageSignatureCache {
87    pub entries: Vec<Option<MessageSignature>>,
88}
89
90impl ClientboundPlayerChat {
91    /// Returns the content of the message. If you want to get the FormattedText
92    /// for the whole message including the sender part, use
93    /// [`ClientboundPlayerChat::message`].
94    #[must_use]
95    pub fn content(&self) -> FormattedText {
96        self.unsigned_content
97            .clone()
98            .unwrap_or_else(|| FormattedText::from(self.body.content.clone()))
99    }
100
101    /// Get the full message, including the sender part.
102    #[must_use]
103    pub fn message(&self) -> FormattedText {
104        let sender = self.chat_type.name.clone();
105        let content = self.content();
106        let target = self.chat_type.target_name.clone();
107
108        let mut args = vec![
109            StringOrComponent::FormattedText(sender),
110            StringOrComponent::FormattedText(content),
111        ];
112        if let Some(target) = target {
113            args.push(StringOrComponent::FormattedText(target));
114        }
115
116        let translation_key = self.chat_type.translation_key();
117        let component = TranslatableComponent::new(translation_key.to_string(), args);
118
119        FormattedText::Translatable(component)
120    }
121}
122
123impl ChatTypeBound {
124    pub fn translation_key(&self) -> &str {
125        match &self.chat_type {
126            Holder::Reference(r) => r.chat_translation_key(),
127            Holder::Direct(d) => d.chat.translation_key.as_str(),
128        }
129    }
130}
131
132impl AzaleaRead for PackedMessageSignature {
133    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
134        let id = u32::azalea_read_var(buf)?;
135        if id == 0 {
136            let full_signature = MessageSignature::azalea_read(buf)?;
137
138            Ok(PackedMessageSignature::Signature(Box::new(full_signature)))
139        } else {
140            Ok(PackedMessageSignature::Id(id - 1))
141        }
142    }
143}
144impl AzaleaWrite for PackedMessageSignature {
145    fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
146        match self {
147            PackedMessageSignature::Signature(full_signature) => {
148                0u32.azalea_write_var(buf)?;
149                full_signature.azalea_write(buf)?;
150            }
151            PackedMessageSignature::Id(id) => {
152                (id + 1).azalea_write_var(buf)?;
153            }
154        }
155        Ok(())
156    }
157}