valence_command/
graph.rs

1//! # Command graph implementation
2//!
3//! This is the core of the command system. It is a graph of `CommandNode`s that
4//! are connected by the `CommandEdgeType`. The graph is used to determine what
5//! command to run when a command is entered. The graph is also used to generate
6//! the command tree that is sent to the client.
7//!
8//! ### The graph is a directed graph with 3 types of nodes:
9//! * Root node ([`NodeData::Root`]) - This is the root of the graph.  It is
10//!   used to connect all the other nodes to the graph. It is always present and
11//!   there should only be one.
12//! * Literal node ([`NodeData::Literal`]) - This is a literal part of a
13//!   command. It is a string that must be matched exactly by the client to
14//!   trigger the validity of the node. For example, the command `/teleport`
15//!   would have a literal node with the name `teleport` which is a child of the
16//!   root node.
17//! * Argument node ([`NodeData::Argument`]) - This is a node that represents an
18//!   argument in a command. It is a string that is matched by the client and
19//!   checked by the server. For example, the command `/teleport 0 0 0` would
20//!   have 1 argument node with the name "<destination:location>" and the parser
21//!   [`Parser::Vec3`] which is a child of the literal node with the name
22//!   `teleport`.
23//!
24//! #### and 2 types of edges:
25//! * Child edge ([`CommandEdgeType::Child`]) - This is an edge that connects a
26//!   parent node to a child node. It is used to determine what nodes are valid
27//!   children of a parent node. for example, the literal node with the name
28//!   `teleport` would have a child edge to the argument node with the name
29//!   "<destination:location>". This means that the argument node is a valid
30//!   child of the literal node.
31//! * Redirect edge ([`CommandEdgeType::Redirect`]) - This edge is special. It
32//!   is used to redirect the client to another node. For example, the literal
33//!   node with the name `tp` would have a Redirect edge to the literal node
34//!   with the name `teleport`. This means that if the client enters the command
35//!   `/tp` the server will redirect the client to the literal node with the
36//!   name `teleport`. Making the command `/tp` functionally equivalent to
37//!   `/teleport`.
38//!
39//! # Cool Example Graph For Possible Implementation Of Teleport Command (made with graphviz)
40//! ```text
41//!                                               ┌────────────────────────────────┐
42//!                                               │              Root              │ ─┐
43//!                                               └────────────────────────────────┘  │
44//!                                                 │                                 │
45//!                                                 │ Child                           │
46//!                                                 ▼                                 │
47//!                                               ┌────────────────────────────────┐  │
48//!                                               │          Literal: tp           │  │
49//!                                               └────────────────────────────────┘  │
50//!                                                 │                                 │
51//!                                                 │ Redirect                        │ Child
52//!                                                 ▼                                 ▼
53//! ┌──────────────────────────────────┐  Child   ┌──────────────────────────────────────────────────────────────────────────────┐
54//! │  Argument: <destination:entity>  │ ◀─────── │                              Literal: teleport                               │
55//! └──────────────────────────────────┘          └──────────────────────────────────────────────────────────────────────────────┘
56//!                                                 │                                           │
57//!                                                 │ Child                                     │ Child
58//!                                                 ▼                                           ▼
59//! ┌──────────────────────────────────┐  Child   ┌────────────────────────────────┐          ┌──────────────────────────────────┐
60//! │ Argument: <destination:location> │ ◀─────── │   Argument: <target:entity>    │          │ Argument: <destination:location> │
61//! └──────────────────────────────────┘          └────────────────────────────────┘          └──────────────────────────────────┘
62//!                                                 │
63//!                                                 │ Child
64//!                                                 ▼
65//!                                               ┌────────────────────────────────┐
66//!                                               │ Argument: <destination:entity> │
67//!                                               └────────────────────────────────┘
68//! ```
69//! If you want a cool graph of your own command graph you can use the display
70//! trait on the [`CommandGraph`] struct. Then you can use a tool like
71//! [Graphviz Online](https://dreampuf.github.io/GraphvizOnline) to look at the graph.
72
73use std::collections::HashMap;
74use std::fmt::{Display, Formatter};
75
76use petgraph::dot::Dot;
77use petgraph::prelude::*;
78use valence_server::protocol::packets::play::command_tree_s2c::{
79    Node, NodeData, Parser, StringArg,
80};
81use valence_server::protocol::packets::play::CommandTreeS2c;
82use valence_server::protocol::VarInt;
83
84use crate::modifier_value::ModifierValue;
85use crate::parsers::{CommandArg, ParseInput};
86use crate::{CommandRegistry, CommandScopeRegistry};
87
88/// This struct is used to store the command graph. (see module level docs for
89/// more info)
90#[derive(Debug, Clone)]
91pub struct CommandGraph {
92    pub graph: Graph<CommandNode, CommandEdgeType>,
93    pub root: NodeIndex,
94}
95
96impl Default for CommandGraph {
97    fn default() -> Self {
98        Self::new()
99    }
100}
101
102/// Output the graph in graphviz dot format to do visual debugging. (this was
103/// used to make the cool graph in the module level docs)
104impl Display for CommandGraph {
105    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
106        write!(f, "{}", Dot::new(&self.graph))
107    }
108}
109
110impl CommandGraph {
111    pub fn new() -> Self {
112        let mut graph = Graph::<CommandNode, CommandEdgeType>::new();
113        let root = graph.add_node(CommandNode {
114            executable: false,
115            data: NodeData::Root,
116            scopes: vec![],
117        });
118
119        Self { graph, root }
120    }
121}
122
123/// Data for the nodes in the graph (see module level docs for more info)
124#[derive(Clone, Debug, PartialEq)]
125pub struct CommandNode {
126    pub executable: bool,
127    pub data: NodeData,
128    pub scopes: Vec<String>,
129}
130
131impl Display for CommandNode {
132    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
133        match &self.data {
134            NodeData::Root => write!(f, "Root"),
135            NodeData::Literal { name } => write!(f, "Literal: {name}"),
136            NodeData::Argument { name, .. } => write!(f, "Argument: <{name}>"),
137        }
138    }
139}
140
141#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
142pub enum CommandEdgeType {
143    Redirect,
144    Child,
145}
146
147impl Display for CommandEdgeType {
148    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
149        match self {
150            CommandEdgeType::Redirect => write!(f, "Redirect"),
151            CommandEdgeType::Child => write!(f, "Child"),
152        }
153    }
154}
155
156impl From<CommandGraph> for CommandTreeS2c {
157    fn from(command_graph: CommandGraph) -> Self {
158        let graph = command_graph.graph;
159        let nodes_and_edges = graph.into_nodes_edges();
160
161        let mut nodes: Vec<Node> = nodes_and_edges
162            .0
163            .into_iter()
164            .map(|node| Node {
165                children: Vec::new(),
166                data: node.weight.data,
167                executable: node.weight.executable,
168                redirect_node: None,
169            })
170            .collect();
171
172        let edges = nodes_and_edges.1;
173
174        for edge in edges {
175            match edge.weight {
176                CommandEdgeType::Child => {
177                    nodes[edge.source().index()]
178                        .children
179                        .push(VarInt::from(edge.target().index() as i32));
180                }
181                CommandEdgeType::Redirect => {
182                    nodes[edge.source().index()].redirect_node =
183                        Some(VarInt::from(edge.target().index() as i32));
184                }
185            }
186        }
187
188        CommandTreeS2c {
189            commands: nodes,
190            root_index: VarInt::from(command_graph.root.index() as i32),
191        }
192    }
193}
194
195/// Ergonomic builder pattern for adding executables, literals and arguments to
196/// a command graph. See the derive macro for a more ergonomic way of doing this
197/// for a basic command with an enum or struct.
198///
199/// # Type Parameters
200/// * `T` - the type that should be constructed by an executable when the
201///   command is executed
202///
203/// # Example
204/// ```
205/// use std::collections::HashMap;
206/// use petgraph::visit::{EdgeCount, NodeCount};
207/// use valence_command::graph::{
208///     CommandGraph, CommandGraphBuilder
209/// };
210/// use valence_command::{CommandRegistry};
211/// use valence_command::parsers::CommandArg;
212///
213/// struct TestCommand {
214///    test: i32,
215/// }
216///
217/// let mut command_graph = CommandRegistry::default();
218/// let mut executable_map = HashMap::new();
219/// let mut parser_map = HashMap::new();
220/// let mut modifier_map = HashMap::new();
221/// let mut command_graph_builder = CommandGraphBuilder::<TestCommand>::new(&mut command_graph, &mut executable_map, &mut parser_map, &mut modifier_map);
222///
223/// // simple command
224/// let simple_command = command_graph_builder
225///    .root() // transition to the root node
226///    .literal("test") // add a literal node then transition to it
227///    .argument("test")
228///    // a player needs one of these scopes to execute the command
229///    // (note: if you want an admin scope you should use the link method on the scope registry.)
230///    .with_scopes(vec!["test:admin", "command:test"])
231///    .with_parser::<i32>()
232///    // it is reasonably safe to unwrap here because we know that the argument is an integer
233///    .with_executable(|args| TestCommand { test: i32::parse_arg(args).unwrap() })
234///    .id();
235///
236/// // complex command (redirects back to the simple command)
237/// command_graph_builder
238///     .root()
239///     .literal("test")
240///     .literal("command")
241///     .redirect_to(simple_command);
242///
243/// assert_eq!(command_graph.graph.graph.node_count(), 5); // root, test, command, <test>, test
244/// // 5 edges, 2 for the simple command, 2 for the complex command and 1 for the redirect
245/// assert_eq!(command_graph.graph.graph.edge_count(), 5);
246/// ```
247///
248/// in this example we can execute either of the following commands for the same
249/// result:
250/// - `/test test 1`
251/// - `/test command test 1`
252///
253/// the executables from these commands will both return a `TestCommand` with
254/// the value `1`
255#[allow(clippy::type_complexity)]
256pub struct CommandGraphBuilder<'a, T> {
257    // We do not own the graph, we just have a mutable reference to it
258    graph: &'a mut CommandGraph,
259    current_node: NodeIndex,
260    executables: &'a mut HashMap<NodeIndex, fn(&mut ParseInput) -> T>,
261    parsers: &'a mut HashMap<NodeIndex, fn(&mut ParseInput) -> bool>,
262    modifiers: &'a mut HashMap<NodeIndex, fn(String, &mut HashMap<ModifierValue, ModifierValue>)>,
263    scopes_added: Vec<String>, /* we need to keep track of added scopes so we can add them to
264                                * the registry later */
265}
266
267impl<'a, T> CommandGraphBuilder<'a, T> {
268    /// Creates a new command graph builder
269    ///
270    /// # Arguments
271    /// * registry - the command registry to add the commands to
272    /// * executables - the map of node indices to executable parser functions
273    #[allow(clippy::type_complexity)]
274    pub fn new(
275        registry: &'a mut CommandRegistry,
276        executables: &'a mut HashMap<NodeIndex, fn(&mut ParseInput) -> T>,
277        parsers: &'a mut HashMap<NodeIndex, fn(&mut ParseInput) -> bool>,
278        modifiers: &'a mut HashMap<
279            NodeIndex,
280            fn(String, &mut HashMap<ModifierValue, ModifierValue>),
281        >,
282    ) -> Self {
283        CommandGraphBuilder {
284            current_node: registry.graph.root,
285            graph: &mut registry.graph,
286            executables,
287            parsers,
288            modifiers,
289            scopes_added: Vec::new(),
290        }
291    }
292
293    /// Transitions to the root node. Use this to start building a new command
294    /// from root.
295    pub fn root(&mut self) -> &mut Self {
296        self.current_node = self.graph.root;
297        self
298    }
299
300    /// Creates a new literal node and transitions to it.
301    ///
302    /// # Default Values
303    /// * executable - `false`
304    /// * scopes - `Vec::new()`
305    pub fn literal<S: Into<String>>(&mut self, literal: S) -> &mut Self {
306        let graph = &mut self.graph.graph;
307        let current_node = &mut self.current_node;
308
309        let literal_node = graph.add_node(CommandNode {
310            executable: false,
311            data: NodeData::Literal {
312                name: literal.into(),
313            },
314            scopes: Vec::new(),
315        });
316
317        graph.add_edge(*current_node, literal_node, CommandEdgeType::Child);
318
319        *current_node = literal_node;
320
321        self
322    }
323
324    /// Creates a new argument node and transitions to it.
325    ///
326    /// # Default Values
327    /// * executable - `false`
328    /// * scopes - `Vec::new()`
329    /// * parser - `StringArg::SingleWord`
330    /// * suggestion - `None`
331    pub fn argument<A: Into<String>>(&mut self, argument: A) -> &mut Self {
332        let graph = &mut self.graph.graph;
333        let current_node = &mut self.current_node;
334
335        let argument_node = graph.add_node(CommandNode {
336            executable: false,
337            data: NodeData::Argument {
338                name: argument.into(),
339                parser: Parser::String(StringArg::SingleWord),
340                suggestion: None,
341            },
342            scopes: Vec::new(),
343        });
344
345        graph.add_edge(*current_node, argument_node, CommandEdgeType::Child);
346
347        *current_node = argument_node;
348
349        self
350    }
351
352    /// Creates a new redirect edge from the current node to the node specified.
353    /// For info on what a redirect edge is, see the module level documentation.
354    ///
355    /// # Example
356    /// ```
357    /// use std::collections::HashMap;
358    ///
359    /// use valence_command::graph::CommandGraphBuilder;
360    /// use valence_command::CommandRegistry;
361    ///
362    /// struct TestCommand;
363    ///
364    /// let mut command_graph = CommandRegistry::default();
365    /// let mut executable_map = HashMap::new();
366    /// let mut parser_map = HashMap::new();
367    /// let mut modifier_map = HashMap::new();
368    /// let mut command_graph_builder = CommandGraphBuilder::<TestCommand>::new(
369    ///     &mut command_graph,
370    ///     &mut executable_map,
371    ///     &mut parser_map,
372    ///     &mut modifier_map,
373    /// );
374    ///
375    /// let simple_command = command_graph_builder
376    ///   .root() // transition to the root node
377    ///   .literal("test") // add a literal node then transition to it
378    ///   .id(); // get the id of the literal node
379    ///
380    /// command_graph_builder
381    ///     .root() // transition to the root node
382    ///     .literal("test") // add a literal node then transition to it
383    ///     .literal("command") // add a literal node then transition to it
384    ///     .redirect_to(simple_command); // redirect to the simple command
385    /// ```
386    pub fn redirect_to(&mut self, node: NodeIndex) -> &mut Self {
387        let graph = &mut self.graph.graph;
388        let current_node = &mut self.current_node;
389
390        graph.add_edge(*current_node, node, CommandEdgeType::Redirect);
391
392        *current_node = node;
393
394        self
395    }
396
397    /// Sets up the executable function for the current node. This function will
398    /// be called when the command is executed and should parse the args and
399    /// return the `T` type.
400    ///
401    /// # Arguments
402    /// * executable - the executable function to add
403    ///
404    /// # Example
405    /// have a look at the example for [`CommandGraphBuilder`]
406    pub fn with_executable(&mut self, executable: fn(&mut ParseInput) -> T) -> &mut Self {
407        let graph = &mut self.graph.graph;
408        let current_node = &mut self.current_node;
409
410        let node = graph.node_weight_mut(*current_node).unwrap();
411
412        node.executable = true;
413        self.executables.insert(*current_node, executable);
414
415        self
416    }
417
418    /// Adds a modifier to the current node
419    ///
420    /// # Arguments
421    /// * modifier - the modifier function to add
422    ///
423    /// # Example
424    /// ```
425    /// use std::collections::HashMap;
426    ///
427    /// use valence_command::graph::CommandGraphBuilder;
428    /// use valence_command::CommandRegistry;
429    ///
430    /// struct TestCommand;
431    ///
432    /// let mut command_graph = CommandRegistry::default();
433    /// let mut executable_map = HashMap::new();
434    /// let mut parser_map = HashMap::new();
435    /// let mut modifier_map = HashMap::new();
436    /// let mut command_graph_builder =
437    ///    CommandGraphBuilder::<TestCommand>::new(&mut command_graph, &mut executable_map, &mut parser_map, &mut modifier_map);
438    ///
439    /// command_graph_builder
440    ///     .root() // transition to the root node
441    ///     .literal("test") // add a literal node then transition to it
442    ///     .with_modifier(|_, modifiers| {
443    ///        modifiers.insert("test".into(), "test".into()); // this will trigger when the node is passed
444    ///     })
445    ///     .literal("command") // add a literal node then transition to it
446    ///     .with_executable(|_| TestCommand);
447    /// ```
448    pub fn with_modifier(
449        &mut self,
450        modifier: fn(String, &mut HashMap<ModifierValue, ModifierValue>),
451    ) -> &mut Self {
452        let current_node = &mut self.current_node;
453
454        self.modifiers.insert(*current_node, modifier);
455
456        self
457    }
458
459    /// Sets the required scopes for the current node
460    ///
461    /// # Arguments
462    /// * scopes - a list of scopes for that are aloud to access a command node
463    ///   and its children (list of strings following the system described in
464    ///   [`scopes`](crate::scopes))
465    pub fn with_scopes<S: Into<String>>(&mut self, scopes: Vec<S>) -> &mut Self {
466        let graph = &mut self.graph.graph;
467        let current_node = &mut self.current_node;
468
469        let node = graph.node_weight_mut(*current_node).unwrap();
470
471        node.scopes = scopes.into_iter().map(|s| s.into()).collect();
472        self.scopes_added.extend(node.scopes.clone());
473
474        self
475    }
476
477    /// Applies the scopes to the registry
478    ///
479    /// # Arguments
480    /// * registry - the registry to apply the scopes to
481    pub fn apply_scopes(&mut self, registry: &mut CommandScopeRegistry) -> &mut Self {
482        for scope in self.scopes_added.clone() {
483            registry.add_scope(scope);
484        }
485        self.scopes_added.clear();
486        self
487    }
488
489    /// Sets the parser for the current node. This will decide how the argument
490    /// is parsed client side and will be used to check the argument before
491    /// it is passed to the executable. The node should be an argument node
492    /// or nothing will happen.
493    ///
494    /// # Type Parameters
495    /// * `P` - the parser to use for the current node (must be [`CommandArg`])
496    pub fn with_parser<P: CommandArg>(&mut self) -> &mut Self {
497        let graph = &mut self.graph.graph;
498        let current_node = self.current_node;
499
500        let node = graph.node_weight_mut(current_node).unwrap();
501        self.parsers
502            .insert(current_node, |input| P::parse_arg(input).is_ok());
503
504        let parser = P::display();
505
506        node.data = match node.data.clone() {
507            NodeData::Argument {
508                name, suggestion, ..
509            } => NodeData::Argument {
510                name,
511                parser,
512                suggestion,
513            },
514            NodeData::Literal { name } => NodeData::Literal { name },
515            NodeData::Root => NodeData::Root,
516        };
517
518        self
519    }
520
521    /// Transitions to the node specified.
522    pub fn at(&mut self, node: NodeIndex) -> &mut Self {
523        self.current_node = node;
524        self
525    }
526
527    /// Gets the id of the current node (useful for commands that have multiple
528    /// children).
529    pub fn id(&self) -> NodeIndex {
530        self.current_node
531    }
532}