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