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