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}
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>::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) -> 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(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)]
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 FormattedText,
115 Style,
116 Message,
117 NbtCompoundTag,
118 NbtTag,
119 NbtPath,
120 Objective,
121 ObjectiveCriteria,
122 Operation,
123 Particle,
124 Angle,
125 Rotation,
126 ScoreboardSlot,
127 ScoreHolder { allows_multiple: bool },
128 Swizzle,
129 Team,
130 ItemSlot,
131 ItemSlots,
132 ResourceLocation,
133 Function,
134 EntityAnchor,
135 IntRange,
136 FloatRange,
137 Dimension,
138 GameMode,
139 Time { min: i32 },
140 ResourceOrTag { registry_key: ResourceLocation },
141 ResourceOrTagKey { registry_key: ResourceLocation },
142 Resource { registry_key: ResourceLocation },
143 ResourceKey { registry_key: ResourceLocation },
144 ResourceSelector { registry_key: ResourceLocation },
145 TemplateMirror,
146 TemplateRotation,
147 Heightmap,
148 LootTable,
149 LootPredicate,
150 LootModifier,
151 Uuid,
152}
153
154#[derive(Debug, Clone, PartialEq, Eq)]
155pub struct EntityParser {
156 pub single: bool,
157 pub players_only: bool,
158}
159impl AzaleaRead for EntityParser {
160 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
161 let flags = FixedBitSet::<2>::azalea_read(buf)?;
162 Ok(EntityParser {
163 single: flags.index(0),
164 players_only: flags.index(1),
165 })
166 }
167}
168impl AzaleaWrite for EntityParser {
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 AzaleaRead 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(5) || flags.index(6) || flags.index(7) {
187 warn!(
188 "The flags from a Brigadier node are over 31. 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
197 let children = Vec::<u32>::azalea_read_var(buf)?;
198 let redirect_node = if has_redirect {
199 Some(u32::azalea_read_var(buf)?)
200 } else {
201 None
202 };
203
204 if node_type == 2 {
206 let name = String::azalea_read(buf)?;
207 let parser = BrigadierParser::azalea_read(buf)?;
208 let suggestions_type = if has_suggestions_type {
209 Some(ResourceLocation::azalea_read(buf)?)
210 } else {
211 None
212 };
213 let node = BrigadierNodeStub {
214 is_executable,
215 children,
216 redirect_node,
217 node_type: NodeType::Argument {
218 name,
219 parser,
220 suggestions_type,
221 },
222 };
223 return Ok(node);
224 }
225 else if node_type == 1 {
227 let name = String::azalea_read(buf)?;
228 return Ok(BrigadierNodeStub {
229 is_executable,
230 children,
231 redirect_node,
232 node_type: NodeType::Literal { name },
233 });
234 }
235 Ok(BrigadierNodeStub {
236 is_executable,
237 children,
238 redirect_node,
239 node_type: NodeType::Root,
240 })
241 }
242}
243
244impl AzaleaWrite for BrigadierNodeStub {
245 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
246 let mut flags = FixedBitSet::<4>::new();
247 if self.is_executable {
248 flags.set(2);
249 }
250 if self.redirect_node.is_some() {
251 flags.set(3);
252 }
253
254 match &self.node_type {
255 NodeType::Root => {
256 flags.azalea_write(buf)?;
257
258 self.children.azalea_write_var(buf)?;
259
260 if let Some(redirect) = self.redirect_node {
261 redirect.azalea_write_var(buf)?;
262 }
263 }
264 NodeType::Literal { name } => {
265 flags.set(0);
266 flags.azalea_write(buf)?;
267
268 self.children.azalea_write_var(buf)?;
269
270 if let Some(redirect) = self.redirect_node {
271 redirect.azalea_write_var(buf)?;
272 }
273
274 name.azalea_write(buf)?;
275 }
276 NodeType::Argument {
277 name,
278 parser,
279 suggestions_type,
280 } => {
281 flags.set(1);
282 if suggestions_type.is_some() {
283 flags.set(4);
284 }
285 flags.azalea_write(buf)?;
286
287 self.children.azalea_write_var(buf)?;
288
289 if let Some(redirect) = self.redirect_node {
290 redirect.azalea_write_var(buf)?;
291 }
292
293 name.azalea_write(buf)?;
294 parser.azalea_write(buf)?;
295
296 if let Some(suggestion) = suggestions_type {
297 suggestion.azalea_write(buf)?;
298 }
299 }
300 }
301 Ok(())
302 }
303}
304
305#[derive(Debug, Clone, PartialEq)]
306pub enum NodeType {
307 Root,
308 Literal {
309 name: String,
310 },
311 Argument {
312 name: String,
313 parser: BrigadierParser,
314 suggestions_type: Option<ResourceLocation>,
315 },
316}
317
318impl BrigadierNodeStub {
319 #[must_use]
320 pub fn name(&self) -> Option<&str> {
321 match &self.node_type {
322 NodeType::Root => None,
323 NodeType::Literal { name } | NodeType::Argument { name, .. } => Some(name),
324 }
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331
332 #[test]
333 fn test_brigadier_node_stub_root() {
334 let data = BrigadierNodeStub {
335 is_executable: false,
336 children: vec![1, 2],
337 redirect_node: None,
338 node_type: NodeType::Root,
339 };
340 let mut buf = Vec::new();
341 data.azalea_write(&mut buf).unwrap();
342 let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
343 let read_data = BrigadierNodeStub::azalea_read(&mut data_cursor).unwrap();
344 assert_eq!(data, read_data);
345 }
346
347 #[test]
348 fn test_brigadier_node_stub_literal() {
349 let data = BrigadierNodeStub {
350 is_executable: true,
351 children: vec![],
352 redirect_node: None,
353 node_type: NodeType::Literal {
354 name: "String".to_string(),
355 },
356 };
357 let mut buf = Vec::new();
358 data.azalea_write(&mut buf).unwrap();
359 let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
360 let read_data = BrigadierNodeStub::azalea_read(&mut data_cursor).unwrap();
361 assert_eq!(data, read_data);
362 }
363
364 #[test]
365 fn test_brigadier_node_stub_argument() {
366 let data = BrigadierNodeStub {
367 is_executable: false,
368 children: vec![6, 9],
369 redirect_node: Some(5),
370 node_type: NodeType::Argument {
371 name: "position".to_string(),
372 parser: BrigadierParser::Vec3,
373 suggestions_type: Some(ResourceLocation::new("minecraft:test_suggestion")),
374 },
375 };
376 let mut buf = Vec::new();
377 data.azalea_write(&mut buf).unwrap();
378 let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);
379 let read_data = BrigadierNodeStub::azalea_read(&mut data_cursor).unwrap();
380 assert_eq!(data, read_data);
381 }
382}