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}