valence_protocol/packets/play/
command_tree_s2c.rs

1use std::borrow::Cow;
2use std::io::Write;
3
4use anyhow::bail;
5use byteorder::WriteBytesExt;
6use valence_ident::Ident;
7
8use crate::{Decode, Encode, Packet, VarInt};
9
10#[derive(Clone, Debug, Encode, Decode, Packet)]
11pub struct CommandTreeS2c {
12    pub commands: Vec<Node>,
13    pub root_index: VarInt,
14}
15
16#[derive(Clone, Debug)]
17pub struct Node {
18    pub data: NodeData,
19    pub executable: bool,
20    pub children: Vec<VarInt>,
21    pub redirect_node: Option<VarInt>,
22}
23
24#[derive(Clone, Debug, PartialEq)]
25pub enum NodeData {
26    Root,
27    Literal {
28        name: String,
29    },
30    Argument {
31        name: String,
32        parser: Parser,
33        suggestion: Option<Suggestion>,
34    },
35}
36
37#[derive(Copy, Clone, PartialEq, Eq, Debug)]
38pub enum Suggestion {
39    AskServer,
40    AllRecipes,
41    AvailableSounds,
42    AvailableBiomes,
43    SummonableEntities,
44}
45
46#[derive(Clone, Debug, PartialEq)]
47pub enum Parser {
48    Bool,
49    Float { min: Option<f32>, max: Option<f32> },
50    Double { min: Option<f64>, max: Option<f64> },
51    Integer { min: Option<i32>, max: Option<i32> },
52    Long { min: Option<i64>, max: Option<i64> },
53    String(StringArg),
54    Entity { single: bool, only_players: bool },
55    GameProfile,
56    BlockPos,
57    ColumnPos,
58    Vec3,
59    Vec2,
60    BlockState,
61    BlockPredicate,
62    ItemStack,
63    ItemPredicate,
64    Color,
65    Component,
66    Message,
67    NbtCompoundTag,
68    NbtTag,
69    NbtPath,
70    Objective,
71    ObjectiveCriteria,
72    Operation,
73    Particle,
74    Angle,
75    Rotation,
76    ScoreboardSlot,
77    ScoreHolder { allow_multiple: bool },
78    Swizzle,
79    Team,
80    ItemSlot,
81    ResourceLocation,
82    Function,
83    EntityAnchor,
84    IntRange,
85    FloatRange,
86    Dimension,
87    GameMode,
88    Time,
89    ResourceOrTag { registry: Ident<String> },
90    ResourceOrTagKey { registry: Ident<String> },
91    Resource { registry: Ident<String> },
92    ResourceKey { registry: Ident<String> },
93    TemplateMirror,
94    TemplateRotation,
95    Uuid,
96}
97
98#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode)]
99pub enum StringArg {
100    SingleWord,
101    QuotablePhrase,
102    GreedyPhrase,
103}
104
105impl Encode for Node {
106    fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
107        let node_type = match &self.data {
108            NodeData::Root => 0,
109            NodeData::Literal { .. } => 1,
110            NodeData::Argument { .. } => 2,
111        };
112
113        let has_suggestion = matches!(
114            &self.data,
115            NodeData::Argument {
116                suggestion: Some(_),
117                ..
118            }
119        );
120
121        let flags: u8 = node_type
122            | (u8::from(self.executable) * 0x04)
123            | (u8::from(self.redirect_node.is_some()) * 0x08)
124            | (u8::from(has_suggestion) * 0x10);
125
126        w.write_u8(flags)?;
127
128        self.children.encode(&mut w)?;
129
130        if let Some(redirect_node) = self.redirect_node {
131            redirect_node.encode(&mut w)?;
132        }
133
134        match &self.data {
135            NodeData::Root => {}
136            NodeData::Literal { name } => {
137                name.encode(&mut w)?;
138            }
139            NodeData::Argument {
140                name,
141                parser,
142                suggestion,
143            } => {
144                name.encode(&mut w)?;
145                parser.encode(&mut w)?;
146
147                if let Some(suggestion) = suggestion {
148                    match suggestion {
149                        Suggestion::AskServer => "ask_server",
150                        Suggestion::AllRecipes => "all_recipes",
151                        Suggestion::AvailableSounds => "available_sounds",
152                        Suggestion::AvailableBiomes => "available_biomes",
153                        Suggestion::SummonableEntities => "summonable_entities",
154                    }
155                    .encode(&mut w)?;
156                }
157            }
158        }
159
160        Ok(())
161    }
162}
163
164impl<'a> Decode<'a> for Node {
165    fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
166        let flags = u8::decode(r)?;
167
168        let children = Vec::decode(r)?;
169
170        let redirect_node = if flags & 0x08 != 0 {
171            Some(VarInt::decode(r)?)
172        } else {
173            None
174        };
175
176        let node_data = match flags & 0x3 {
177            0 => NodeData::Root,
178            1 => NodeData::Literal {
179                name: <String>::decode(r)?,
180            },
181            2 => NodeData::Argument {
182                name: <String>::decode(r)?,
183                parser: Parser::decode(r)?,
184                suggestion: if flags & 0x10 != 0 {
185                    Some(match Ident::<Cow<str>>::decode(r)?.as_str() {
186                        "minecraft:ask_server" => Suggestion::AskServer,
187                        "minecraft:all_recipes" => Suggestion::AllRecipes,
188                        "minecraft:available_sounds" => Suggestion::AvailableSounds,
189                        "minecraft:available_biomes" => Suggestion::AvailableBiomes,
190                        "minecraft:summonable_entities" => Suggestion::SummonableEntities,
191                        other => bail!("unknown command suggestion type of \"{other}\""),
192                    })
193                } else {
194                    None
195                },
196            },
197            n => bail!("invalid node type of {n}"),
198        };
199
200        Ok(Self {
201            children,
202            data: node_data,
203            executable: flags & 0x04 != 0,
204            redirect_node,
205        })
206    }
207}
208
209impl Encode for Parser {
210    fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
211        match self {
212            Parser::Bool => 0_u8.encode(&mut w)?,
213            Parser::Float { min, max } => {
214                1_u8.encode(&mut w)?;
215
216                (u8::from(min.is_some()) | (u8::from(max.is_some()) * 0x2)).encode(&mut w)?;
217
218                if let Some(min) = min {
219                    min.encode(&mut w)?;
220                }
221
222                if let Some(max) = max {
223                    max.encode(&mut w)?;
224                }
225            }
226            Parser::Double { min, max } => {
227                2_u8.encode(&mut w)?;
228
229                (u8::from(min.is_some()) | (u8::from(max.is_some()) * 0x2)).encode(&mut w)?;
230
231                if let Some(min) = min {
232                    min.encode(&mut w)?;
233                }
234
235                if let Some(max) = max {
236                    max.encode(&mut w)?;
237                }
238            }
239            Parser::Integer { min, max } => {
240                3_u8.encode(&mut w)?;
241
242                (u8::from(min.is_some()) | (u8::from(max.is_some()) * 0x2)).encode(&mut w)?;
243
244                if let Some(min) = min {
245                    min.encode(&mut w)?;
246                }
247
248                if let Some(max) = max {
249                    max.encode(&mut w)?;
250                }
251            }
252            Parser::Long { min, max } => {
253                4_u8.encode(&mut w)?;
254
255                (u8::from(min.is_some()) | (u8::from(max.is_some()) * 0x2)).encode(&mut w)?;
256
257                if let Some(min) = min {
258                    min.encode(&mut w)?;
259                }
260
261                if let Some(max) = max {
262                    max.encode(&mut w)?;
263                }
264            }
265            Parser::String(arg) => {
266                5_u8.encode(&mut w)?;
267                arg.encode(&mut w)?;
268            }
269            Parser::Entity {
270                single,
271                only_players,
272            } => {
273                6_u8.encode(&mut w)?;
274                (u8::from(*single) | (u8::from(*only_players) * 0x2)).encode(&mut w)?;
275            }
276            Parser::GameProfile => 7_u8.encode(&mut w)?,
277            Parser::BlockPos => 8_u8.encode(&mut w)?,
278            Parser::ColumnPos => 9_u8.encode(&mut w)?,
279            Parser::Vec3 => 10_u8.encode(&mut w)?,
280            Parser::Vec2 => 11_u8.encode(&mut w)?,
281            Parser::BlockState => 12_u8.encode(&mut w)?,
282            Parser::BlockPredicate => 13_u8.encode(&mut w)?,
283            Parser::ItemStack => 14_u8.encode(&mut w)?,
284            Parser::ItemPredicate => 15_u8.encode(&mut w)?,
285            Parser::Color => 16_u8.encode(&mut w)?,
286            Parser::Component => 17_u8.encode(&mut w)?,
287            Parser::Message => 18_u8.encode(&mut w)?,
288            Parser::NbtCompoundTag => 19_u8.encode(&mut w)?,
289            Parser::NbtTag => 20_u8.encode(&mut w)?,
290            Parser::NbtPath => 21_u8.encode(&mut w)?,
291            Parser::Objective => 22_u8.encode(&mut w)?,
292            Parser::ObjectiveCriteria => 23_u8.encode(&mut w)?,
293            Parser::Operation => 24_u8.encode(&mut w)?,
294            Parser::Particle => 25_u8.encode(&mut w)?,
295            Parser::Angle => 26_u8.encode(&mut w)?,
296            Parser::Rotation => 27_u8.encode(&mut w)?,
297            Parser::ScoreboardSlot => 28_u8.encode(&mut w)?,
298            Parser::ScoreHolder { allow_multiple } => {
299                29_u8.encode(&mut w)?;
300                allow_multiple.encode(&mut w)?;
301            }
302            Parser::Swizzle => 30_u8.encode(&mut w)?,
303            Parser::Team => 31_u8.encode(&mut w)?,
304            Parser::ItemSlot => 32_u8.encode(&mut w)?,
305            Parser::ResourceLocation => 33_u8.encode(&mut w)?,
306            Parser::Function => 34_u8.encode(&mut w)?,
307            Parser::EntityAnchor => 35_u8.encode(&mut w)?,
308            Parser::IntRange => 36_u8.encode(&mut w)?,
309            Parser::FloatRange => 37_u8.encode(&mut w)?,
310            Parser::Dimension => 38_u8.encode(&mut w)?,
311            Parser::GameMode => 39_u8.encode(&mut w)?,
312            Parser::Time => 40_u8.encode(&mut w)?,
313            Parser::ResourceOrTag { registry } => {
314                41_u8.encode(&mut w)?;
315                registry.encode(&mut w)?;
316            }
317            Parser::ResourceOrTagKey { registry } => {
318                42_u8.encode(&mut w)?;
319                registry.encode(&mut w)?;
320            }
321            Parser::Resource { registry } => {
322                43_u8.encode(&mut w)?;
323                registry.encode(&mut w)?;
324            }
325            Parser::ResourceKey { registry } => {
326                44_u8.encode(&mut w)?;
327                registry.encode(&mut w)?;
328            }
329            Parser::TemplateMirror => 45_u8.encode(&mut w)?,
330            Parser::TemplateRotation => 46_u8.encode(&mut w)?,
331            Parser::Uuid => 47_u8.encode(&mut w)?,
332        }
333
334        Ok(())
335    }
336}
337
338impl<'a> Decode<'a> for Parser {
339    fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
340        fn decode_min_max<'a, T: Decode<'a>>(
341            r: &mut &'a [u8],
342        ) -> anyhow::Result<(Option<T>, Option<T>)> {
343            let flags = u8::decode(r)?;
344
345            let min = if flags & 0x1 != 0 {
346                Some(T::decode(r)?)
347            } else {
348                None
349            };
350
351            let max = if flags & 0x2 != 0 {
352                Some(T::decode(r)?)
353            } else {
354                None
355            };
356
357            Ok((min, max))
358        }
359
360        Ok(match u8::decode(r)? {
361            0 => Self::Bool,
362            1 => {
363                let (min, max) = decode_min_max(r)?;
364                Self::Float { min, max }
365            }
366            2 => {
367                let (min, max) = decode_min_max(r)?;
368                Self::Double { min, max }
369            }
370            3 => {
371                let (min, max) = decode_min_max(r)?;
372                Self::Integer { min, max }
373            }
374            4 => {
375                let (min, max) = decode_min_max(r)?;
376                Self::Long { min, max }
377            }
378            5 => Self::String(StringArg::decode(r)?),
379            6 => {
380                let flags = u8::decode(r)?;
381                Self::Entity {
382                    single: flags & 0x1 != 0,
383                    only_players: flags & 0x2 != 0,
384                }
385            }
386            7 => Self::GameProfile,
387            8 => Self::BlockPos,
388            9 => Self::ColumnPos,
389            10 => Self::Vec3,
390            11 => Self::Vec2,
391            12 => Self::BlockState,
392            13 => Self::BlockPredicate,
393            14 => Self::ItemStack,
394            15 => Self::ItemPredicate,
395            16 => Self::Color,
396            17 => Self::Component,
397            18 => Self::Message,
398            19 => Self::NbtCompoundTag,
399            20 => Self::NbtTag,
400            21 => Self::NbtPath,
401            22 => Self::Objective,
402            23 => Self::ObjectiveCriteria,
403            24 => Self::Operation,
404            25 => Self::Particle,
405            26 => Self::Angle,
406            27 => Self::Rotation,
407            28 => Self::ScoreboardSlot,
408            29 => Self::ScoreHolder {
409                allow_multiple: bool::decode(r)?,
410            },
411            30 => Self::Swizzle,
412            31 => Self::Team,
413            32 => Self::ItemSlot,
414            33 => Self::ResourceLocation,
415            34 => Self::Function,
416            35 => Self::EntityAnchor,
417            36 => Self::IntRange,
418            37 => Self::FloatRange,
419            38 => Self::Dimension,
420            39 => Self::GameMode,
421            40 => Self::Time,
422            41 => Self::ResourceOrTag {
423                registry: Ident::decode(r)?,
424            },
425            42 => Self::ResourceOrTagKey {
426                registry: Ident::decode(r)?,
427            },
428            43 => Self::Resource {
429                registry: Ident::decode(r)?,
430            },
431            44 => Self::ResourceKey {
432                registry: Ident::decode(r)?,
433            },
434            45 => Self::TemplateMirror,
435            46 => Self::TemplateRotation,
436            47 => Self::Uuid,
437            n => bail!("unknown command parser ID of {n}"),
438        })
439    }
440}