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}