1use std::io::{self, Cursor, Write};
2
3use azalea_buf::{AzBuf, AzBufVar, BufReadError};
4use azalea_core::bitset::FixedBitSet;
5use azalea_protocol_macros::ClientboundGamePacket;
6use azalea_registry::identifier::Identifier;
7use tracing::warn;
8
9#[derive(AzBuf, ClientboundGamePacket, Clone, Debug, PartialEq)]
10pub struct ClientboundCommands {
11 pub entries: Vec<BrigadierNodeStub>,
12 #[var]
13 pub root_index: u32,
14}
15
16#[derive(Clone, Debug, PartialEq)]
17pub struct BrigadierNodeStub {
18 pub is_executable: bool,
19 pub children: Vec<u32>,
20 pub redirect_node: Option<u32>,
21 pub node_type: NodeType,
22 pub is_restricted: bool,
23}
24
25#[derive(Clone, Debug, Eq)]
26pub struct BrigadierNumber<T> {
27 pub min: Option<T>,
28 pub max: Option<T>,
29}
30impl<T> BrigadierNumber<T> {
31 pub fn new(min: Option<T>, max: Option<T>) -> BrigadierNumber<T> {
32 BrigadierNumber { min, max }
33 }
34}
35impl<T: PartialEq> PartialEq for BrigadierNumber<T> {
36 fn eq(&self, other: &Self) -> bool {
37 match (&self.min, &self.max, &other.min, &other.max) {
38 (Some(f_min), None, Some(s_min), None) => f_min == s_min,
39 (None, Some(f_max), None, Some(s_max)) => f_max == s_max,
40 (Some(f_min), Some(f_max), Some(s_min), Some(s_max)) => {
41 f_min == s_min && f_max == s_max
42 }
43 (None, None, None, None) => true,
44 _ => false,
45 }
46 }
47}
48
49impl<T: AzBuf> AzBuf for BrigadierNumber<T> {
50 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
51 let flags = FixedBitSet::<2>::azalea_read(buf)?;
52 let min = if flags.index(0) {
53 Some(T::azalea_read(buf)?)
54 } else {
55 None
56 };
57 let max = if flags.index(1) {
58 Some(T::azalea_read(buf)?)
59 } else {
60 None
61 };
62 Ok(BrigadierNumber { min, max })
63 }
64 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
65 let mut flags = FixedBitSet::<2>::new();
66 if self.min.is_some() {
67 flags.set(0);
68 }
69 if self.max.is_some() {
70 flags.set(1);
71 }
72 flags.azalea_write(buf)?;
73 if let Some(min) = &self.min {
74 min.azalea_write(buf)?;
75 }
76 if let Some(max) = &self.max {
77 max.azalea_write(buf)?;
78 }
79 Ok(())
80 }
81}
82
83#[derive(AzBuf, Clone, Copy, Debug, Eq, PartialEq)]
84pub enum BrigadierString {
85 SingleWord = 0,
87 QuotablePhrase = 1,
90 GreedyPhrase = 2,
92}
93
94#[derive(AzBuf, Clone, Debug, PartialEq)]
96pub enum BrigadierParser {
97 Bool,
98 Float(BrigadierNumber<f32>),
99 Double(BrigadierNumber<f64>),
100 Integer(BrigadierNumber<i32>),
101 Long(BrigadierNumber<i64>),
102 String(BrigadierString),
103 Entity(EntityParser),
104 GameProfile,
105 BlockPos,
106 ColumnPos,
107 Vec3,
108 Vec2,
109 BlockState,
110 BlockPredicate,
111 ItemStack,
112 ItemPredicate,
113 Color,
114 HexColor,
115 FormattedText,
116 Style,
117 Message,
118 NbtCompoundTag,
119 NbtTag,
120 NbtPath,
121 Objective,
122 ObjectiveCriteria,
123 Operation,
124 Particle,
125 Angle,
126 Rotation,
127 ScoreboardSlot,
128 ScoreHolder { allows_multiple: bool },
129 Swizzle,
130 Team,
131 ItemSlot,
132 ItemSlots,
133 Identifier,
134 Function,
135 EntityAnchor,
136 IntRange,
137 FloatRange,
138 Dimension,
139 GameMode,
140 Time { min: i32 },
141 ResourceOrTag { registry_key: Identifier },
142 ResourceOrTagKey { registry_key: Identifier },
143 Resource { registry_key: Identifier },
144 ResourceKey { registry_key: Identifier },
145 ResourceSelector { registry_key: Identifier },
146 TemplateMirror,
147 TemplateRotation,
148 Heightmap,
149 LootTable,
150 LootPredicate,
151 LootModifier,
152 Dialog,
153 Uuid,
154}
155
156#[derive(Clone, Debug, Eq, PartialEq)]
157pub struct EntityParser {
158 pub single: bool,
159 pub players_only: bool,
160}
161impl AzBuf for EntityParser {
162 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
163 let flags = FixedBitSet::<2>::azalea_read(buf)?;
164 Ok(EntityParser {
165 single: flags.index(0),
166 players_only: flags.index(1),
167 })
168 }
169 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
170 let mut flags = FixedBitSet::<2>::new();
171 if self.single {
172 flags.set(0);
173 }
174 if self.players_only {
175 flags.set(1);
176 }
177 flags.azalea_write(buf)?;
178 Ok(())
179 }
180}
181
182impl AzBuf for BrigadierNodeStub {
184 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
185 let flags = FixedBitSet::<8>::azalea_read(buf)?;
186 if flags.index(6) || flags.index(7) {
187 warn!(
188 "The flags from a Brigadier node are over 63. This is a bug, BrigadierParser probably needs updating.",
189 );
190 }
191
192 let node_type = u8::from(flags.index(0)) + (u8::from(flags.index(1)) * 2);
193 let is_executable = flags.index(2);
194 let has_redirect = flags.index(3);
195 let has_suggestions_type = flags.index(4);
196 let is_restricted = flags.index(5);
197
198 let children = Vec::<u32>::azalea_read_var(buf)?;
199 let redirect_node = if has_redirect {
200 Some(u32::azalea_read_var(buf)?)
201 } else {
202 None
203 };
204
205 if node_type == 2 {
207 let name = String::azalea_read(buf)?;
208 let parser = BrigadierParser::azalea_read(buf)?;
209 let suggestions_type = if has_suggestions_type {
210 Some(Identifier::azalea_read(buf)?)
211 } else {
212 None
213 };
214 Ok(BrigadierNodeStub {
215 is_executable,
216 children,
217 redirect_node,
218 node_type: NodeType::Argument {
219 name,
220 parser,
221 suggestions_type,
222 },
223 is_restricted,
224 })
225 }
226 else if node_type == 1 {
228 let name = String::azalea_read(buf)?;
229 Ok(BrigadierNodeStub {
230 is_executable,
231 children,
232 redirect_node,
233 node_type: NodeType::Literal { name },
234 is_restricted,
235 })
236 } else {
237 Ok(BrigadierNodeStub {
238 is_executable,
239 children,
240 redirect_node,
241 node_type: NodeType::Root,
242 is_restricted,
243 })
244 }
245 }
246 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
247 let mut flags = FixedBitSet::<4>::new();
248 if self.is_executable {
249 flags.set(2);
250 }
251 if self.redirect_node.is_some() {
252 flags.set(3);
253 }
254
255 match &self.node_type {
256 NodeType::Root => {
257 flags.azalea_write(buf)?;
258
259 self.children.azalea_write_var(buf)?;
260
261 if let Some(redirect) = self.redirect_node {
262 redirect.azalea_write_var(buf)?;
263 }
264 }
265 NodeType::Literal { name } => {
266 flags.set(0);
267 flags.azalea_write(buf)?;
268
269 self.children.azalea_write_var(buf)?;
270
271 if let Some(redirect) = self.redirect_node {
272 redirect.azalea_write_var(buf)?;
273 }
274
275 name.azalea_write(buf)?;
276 }
277 NodeType::Argument {
278 name,
279 parser,
280 suggestions_type,
281 } => {
282 flags.set(1);
283 if suggestions_type.is_some() {
284 flags.set(4);
285 }
286 flags.azalea_write(buf)?;
287
288 self.children.azalea_write_var(buf)?;
289
290 if let Some(redirect) = self.redirect_node {
291 redirect.azalea_write_var(buf)?;
292 }
293
294 name.azalea_write(buf)?;
295 parser.azalea_write(buf)?;
296
297 if let Some(suggestion) = suggestions_type {
298 suggestion.azalea_write(buf)?;
299 }
300 }
301 }
302 Ok(())
303 }
304}
305
306#[derive(Clone, Debug, PartialEq)]
307pub enum NodeType {
308 Root,
309 Literal {
310 name: String,
311 },
312 Argument {
313 name: String,
314 parser: BrigadierParser,
315 suggestions_type: Option<Identifier>,
316 },
317}
318
319impl BrigadierNodeStub {
320 #[must_use]
321 pub fn name(&self) -> Option<&str> {
322 match &self.node_type {
323 NodeType::Root => None,
324 NodeType::Literal { name } | NodeType::Argument { name, .. } => Some(name),
325 }
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332
333 #[test]
334 fn test_brigadier_node_stub_root() {
335 let data = BrigadierNodeStub {
336 is_executable: false,
337 children: vec![1, 2],
338 redirect_node: None,
339 node_type: NodeType::Root,
340 is_restricted: false,
341 };
342 let mut buf = Vec::new();
343 data.azalea_write(&mut buf).unwrap();
344 let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
345 let read_data = BrigadierNodeStub::azalea_read(&mut data_cursor).unwrap();
346 assert_eq!(data, read_data);
347 }
348
349 #[test]
350 fn test_brigadier_node_stub_literal() {
351 let data = BrigadierNodeStub {
352 is_executable: true,
353 children: vec![],
354 redirect_node: None,
355 node_type: NodeType::Literal {
356 name: "String".to_owned(),
357 },
358 is_restricted: false,
359 };
360 let mut buf = Vec::new();
361 data.azalea_write(&mut buf).unwrap();
362 let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
363 let read_data = BrigadierNodeStub::azalea_read(&mut data_cursor).unwrap();
364 assert_eq!(data, read_data);
365 }
366
367 #[test]
368 fn test_brigadier_node_stub_argument() {
369 let data = BrigadierNodeStub {
370 is_executable: false,
371 children: vec![6, 9],
372 redirect_node: Some(5),
373 node_type: NodeType::Argument {
374 name: "position".to_owned(),
375 parser: BrigadierParser::Vec3,
376 suggestions_type: Some(Identifier::new("minecraft:test_suggestion")),
377 },
378 is_restricted: false,
379 };
380 let mut buf = Vec::new();
381 data.azalea_write(&mut buf).unwrap();
382 let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
383 let read_data = BrigadierNodeStub::azalea_read(&mut data_cursor).unwrap();
384 assert_eq!(data, read_data);
385 }
386}