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