azalea_protocol/packets/game/
c_player_chat.rs

1use std::io::{self, Cursor, Write};
2
3use azalea_buf::{AzBuf, AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
4use azalea_chat::{
5    FormattedText,
6    translatable_component::{PrimitiveOrComponent, 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, PartialEq, ClientboundGamePacket)]
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.
92    ///
93    /// If you want to get the [`FormattedText`] for the whole message including
94    /// the sender part, use [`ClientboundPlayerChat::message`].
95    #[must_use]
96    pub fn content(&self) -> FormattedText {
97        self.unsigned_content
98            .clone()
99            .unwrap_or_else(|| FormattedText::from(self.body.content.clone()))
100    }
101
102    /// Get the full message, including the sender part.
103    #[must_use]
104    pub fn message(&self) -> FormattedText {
105        let sender = self.chat_type.name.clone();
106        let content = self.content();
107        let target = self.chat_type.target_name.clone();
108
109        let mut args = vec![
110            PrimitiveOrComponent::FormattedText(sender),
111            PrimitiveOrComponent::FormattedText(content),
112        ];
113        if let Some(target) = target {
114            args.push(PrimitiveOrComponent::FormattedText(target));
115        }
116
117        let translation_key = self.chat_type.translation_key();
118        let component = TranslatableComponent::new(translation_key.to_string(), args);
119
120        FormattedText::Translatable(component)
121    }
122}
123
124impl ChatTypeBound {
125    pub fn translation_key(&self) -> &str {
126        match &self.chat_type {
127            Holder::Reference(r) => r.chat_translation_key(),
128            Holder::Direct(d) => d.chat.translation_key.as_str(),
129        }
130    }
131}
132
133impl AzaleaRead for PackedMessageSignature {
134    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
135        let id = u32::azalea_read_var(buf)?;
136        if id == 0 {
137            let full_signature = MessageSignature::azalea_read(buf)?;
138
139            Ok(PackedMessageSignature::Signature(Box::new(full_signature)))
140        } else {
141            Ok(PackedMessageSignature::Id(id - 1))
142        }
143    }
144}
145impl AzaleaWrite for PackedMessageSignature {
146    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
147        match self {
148            PackedMessageSignature::Signature(full_signature) => {
149                0u32.azalea_write_var(buf)?;
150                full_signature.azalea_write(buf)?;
151            }
152            PackedMessageSignature::Id(id) => {
153                (id + 1).azalea_write_var(buf)?;
154            }
155        }
156        Ok(())
157    }
158}