Skip to main content

mcproto_types/
chat_type.rs

1//! Minecraft protocol direct chat types.
2
3use mcproto_codec::error::{CodecError, CodecKind};
4
5use crate::{
6    ProtocolEnum, TypeCodec,
7    basic::{PrefixedString, VarInt},
8    contextual::PrefixedArray,
9    nbt::Nbt,
10};
11
12/// A value inserted into a chat decoration's translated message.
13///
14/// Each parameter is encoded as a [`VarInt`]. Its position in the parameter
15/// array determines the corresponding argument passed to the translation.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
17#[protocol_enum(repr = VarInt)]
18pub enum ChatTypeParameter {
19    /// The component representing the message sender.
20    Sender = 0,
21    /// The component representing the message target.
22    Target = 1,
23    /// The component containing the message content.
24    Content = 2,
25}
26
27/// Controls how one form of a direct chat message is decorated.
28///
29/// A decoration is encoded as its translation key, a length-prefixed array of
30/// [`ChatTypeParameter`] values, and an [`Nbt`] style. The style is always
31/// present on the wire; an unstyled decoration uses an empty NBT compound.
32#[derive(Debug, Clone, PartialEq)]
33pub struct ChatDecoration {
34    /// Translation key used to format the message.
35    pub translation_key: PrefixedString,
36    /// Ordered arguments supplied to the translated message.
37    pub parameters: PrefixedArray<ChatTypeParameter>,
38    /// Text style encoded as network NBT.
39    pub style: Nbt,
40}
41
42impl ChatDecoration {
43    /// Creates a chat decoration from its translation key, parameters, and style.
44    #[must_use]
45    pub const fn new(
46        translation_key: PrefixedString,
47        parameters: PrefixedArray<ChatTypeParameter>,
48        style: Nbt,
49    ) -> Self {
50        Self {
51            translation_key,
52            parameters,
53            style,
54        }
55    }
56}
57
58impl TypeCodec for ChatDecoration {
59    fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
60        self.translation_key
61            .encode(writer)
62            .map_err(|error| error.with_context(CodecKind::ChatDecoration))?;
63        self.parameters
64            .encode(writer)
65            .map_err(|error| error.with_context(CodecKind::ChatDecoration))?;
66        self.style
67            .encode(writer)
68            .map_err(|error| error.with_context(CodecKind::ChatDecoration))
69    }
70
71    fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
72        let translation_key = PrefixedString::decode(reader)
73            .map_err(|error| error.with_context(CodecKind::ChatDecoration))?;
74        let parameters = PrefixedArray::<ChatTypeParameter>::decode(reader)
75            .map_err(|error| error.with_context(CodecKind::ChatDecoration))?;
76        let style =
77            Nbt::decode(reader).map_err(|error| error.with_context(CodecKind::ChatDecoration))?;
78        Ok(Self::new(translation_key, parameters, style))
79    }
80}
81
82/// Describes a direct chat type that a message can be sent with.
83///
84/// The chat decoration is encoded first and the narration decoration second:
85///
86/// ```text
87/// ChatDecoration(chat) + ChatDecoration(narration)
88/// ```
89///
90/// Both fields use the same decoration structure, but may use different
91/// translation keys, parameter orders, and styles.
92///
93/// # Examples
94///
95/// ```
96/// use fastnbt::nbt;
97/// use mcproto_types::{
98///     ChatDecoration, ChatType, ChatTypeParameter, Nbt, PrefixedArray,
99///     PrefixedString, TypeCodec,
100/// };
101///
102/// let value = ChatType::new(
103///     ChatDecoration::new(
104///         PrefixedString("chat.type.text".into()),
105///         PrefixedArray(vec![ChatTypeParameter::Sender, ChatTypeParameter::Content]),
106///         Nbt(nbt!({})),
107///     ),
108///     ChatDecoration::new(
109///         PrefixedString("chat.type.text.narrate".into()),
110///         PrefixedArray(vec![ChatTypeParameter::Sender, ChatTypeParameter::Content]),
111///         Nbt(nbt!({})),
112///     ),
113/// );
114///
115/// let mut encoded = Vec::new();
116/// value.encode(&mut encoded)?;
117/// let mut input = encoded.as_slice();
118/// assert_eq!(ChatType::decode(&mut input)?, value);
119/// assert!(input.is_empty());
120/// # Ok::<(), mcproto_codec::error::CodecError>(())
121/// ```
122#[derive(Debug, Clone, PartialEq)]
123pub struct ChatType {
124    /// Decoration used for the normal chat presentation.
125    pub chat: ChatDecoration,
126    /// Decoration used for narration.
127    pub narration: ChatDecoration,
128}
129
130impl ChatType {
131    /// Creates a direct chat type from its chat and narration decorations.
132    #[must_use]
133    pub const fn new(chat: ChatDecoration, narration: ChatDecoration) -> Self {
134        Self { chat, narration }
135    }
136}
137
138impl TypeCodec for ChatType {
139    fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
140        self.chat
141            .encode(writer)
142            .map_err(|error| error.with_context(CodecKind::ChatType))?;
143        self.narration
144            .encode(writer)
145            .map_err(|error| error.with_context(CodecKind::ChatType))
146    }
147
148    fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
149        let chat = ChatDecoration::decode(reader)
150            .map_err(|error| error.with_context(CodecKind::ChatType))?;
151        let narration = ChatDecoration::decode(reader)
152            .map_err(|error| error.with_context(CodecKind::ChatType))?;
153        Ok(Self::new(chat, narration))
154    }
155}