valence_command/
manager.rs

1use std::collections::{HashMap, HashSet};
2
3use bevy_app::{App, Plugin, PreUpdate};
4use bevy_ecs::entity::Entity;
5use bevy_ecs::prelude::{
6    Added, Changed, Commands, DetectChanges, Event, EventReader, EventWriter, IntoSystemConfigs,
7    Mut, Or, Query, Res,
8};
9use petgraph::graph::NodeIndex;
10use petgraph::prelude::EdgeRef;
11use petgraph::{Direction, Graph};
12use tracing::{debug, info, trace, warn};
13use valence_server::client::{Client, SpawnClientsSet};
14use valence_server::event_loop::PacketEvent;
15use valence_server::protocol::packets::play::command_tree_s2c::NodeData;
16use valence_server::protocol::packets::play::{CommandExecutionC2s, CommandTreeS2c};
17use valence_server::protocol::WritePacket;
18use valence_server::EventLoopPreUpdate;
19
20use crate::graph::{CommandEdgeType, CommandGraph, CommandNode};
21use crate::parsers::ParseInput;
22use crate::scopes::{CommandScopePlugin, CommandScopes};
23use crate::{CommandRegistry, CommandScopeRegistry, CommandSystemSet, ModifierValue};
24
25pub struct CommandPlugin;
26
27impl Plugin for CommandPlugin {
28    fn build(&self, app: &mut App) {
29        app.add_plugins(CommandScopePlugin)
30            .add_event::<CommandExecutionEvent>()
31            .add_event::<CommandProcessedEvent>()
32            .add_systems(PreUpdate, insert_scope_component.after(SpawnClientsSet))
33            .add_systems(
34                EventLoopPreUpdate,
35                (
36                    update_command_tree,
37                    command_tree_update_with_client,
38                    read_incoming_packets.before(CommandSystemSet),
39                    parse_incoming_commands.in_set(CommandSystemSet),
40                ),
41            );
42
43        let graph: CommandGraph = CommandGraph::new();
44        let modifiers = HashMap::new();
45        let parsers = HashMap::new();
46        let executables = HashSet::new();
47
48        app.insert_resource(CommandRegistry {
49            graph,
50            parsers,
51            modifiers,
52            executables,
53        });
54    }
55}
56
57/// This event is sent when a command is sent (you can send this with any
58/// entity)
59#[derive(Debug, Clone, PartialEq, Eq, Hash, Event)]
60pub struct CommandExecutionEvent {
61    /// the command that was executed eg. "teleport @p 0 ~ 0"
62    pub command: String,
63    /// usually the Client entity but it could be a command block or something
64    /// (whatever the library user wants)
65    pub executor: Entity,
66}
67
68/// This will only be sent if the command was successfully parsed and an
69/// executable was found
70#[derive(Debug, Clone, PartialEq, Eq, Event)]
71pub struct CommandProcessedEvent {
72    /// the command that was executed eg. "teleport @p 0 ~ 0"
73    pub command: String,
74    /// usually the Client entity but it could be a command block or something
75    /// (whatever the library user wants)
76    pub executor: Entity,
77    /// the modifiers that were applied to the command
78    pub modifiers: HashMap<ModifierValue, ModifierValue>,
79    /// the node that was executed
80    pub node: NodeIndex,
81}
82
83fn insert_scope_component(mut clients: Query<Entity, Added<Client>>, mut commands: Commands) {
84    for client in &mut clients {
85        commands.entity(client).insert(CommandScopes::new());
86    }
87}
88
89fn read_incoming_packets(
90    mut packets: EventReader<PacketEvent>,
91    mut event_writer: EventWriter<CommandExecutionEvent>,
92) {
93    for packet in packets.read() {
94        let client = packet.client;
95        if let Some(packet) = packet.decode::<CommandExecutionC2s>() {
96            event_writer.send(CommandExecutionEvent {
97                command: packet.command.to_string(),
98                executor: client,
99            });
100        }
101    }
102}
103
104#[allow(clippy::type_complexity)]
105fn command_tree_update_with_client(
106    command_registry: Res<CommandRegistry>,
107    scope_registry: Res<CommandScopeRegistry>,
108    mut updated_clients: Query<
109        (&mut Client, &CommandScopes),
110        Or<(Added<Client>, Changed<CommandScopes>)>,
111    >,
112) {
113    update_client_command_tree(
114        &command_registry,
115        scope_registry,
116        &mut updated_clients.iter_mut().collect(),
117    );
118}
119
120fn update_command_tree(
121    command_registry: Res<CommandRegistry>,
122    scope_registry: Res<CommandScopeRegistry>,
123    mut clients: Query<(&mut Client, &CommandScopes)>,
124) {
125    if command_registry.is_changed() {
126        update_client_command_tree(
127            &command_registry,
128            scope_registry,
129            &mut clients.iter_mut().collect(),
130        );
131    }
132}
133
134fn update_client_command_tree(
135    command_registry: &Res<CommandRegistry>,
136    scope_registry: Res<CommandScopeRegistry>,
137    updated_clients: &mut Vec<(Mut<Client>, &CommandScopes)>,
138) {
139    for (ref mut client, client_scopes) in updated_clients {
140        let time = std::time::Instant::now();
141
142        let old_graph = &command_registry.graph;
143        let mut new_graph = Graph::new();
144
145        // collect a new graph into only nodes that are allowed to be executed
146        let root = old_graph.root;
147
148        let mut to_visit = vec![(None, root)];
149        let mut already_visited = HashSet::new(); // prevent recursion
150        let mut old_to_new = HashMap::new();
151        let mut new_root = None;
152
153        while let Some((parent, node)) = to_visit.pop() {
154            if already_visited.contains(&(parent.map(|(node_id, _)| node_id), node)) {
155                continue;
156            }
157            already_visited.insert((parent.map(|(node_id, _)| node_id), node));
158            let node_scopes = &old_graph.graph[node].scopes;
159            if !node_scopes.is_empty() {
160                let mut has_scope = false;
161                for scope in node_scopes {
162                    if scope_registry.any_grants(
163                        &client_scopes.0.iter().map(|scope| scope.as_str()).collect(),
164                        scope,
165                    ) {
166                        has_scope = true;
167                        break;
168                    }
169                }
170                if !has_scope {
171                    continue;
172                }
173            }
174
175            let new_node = *old_to_new
176                .entry(node)
177                .or_insert_with(|| new_graph.add_node(old_graph.graph[node].clone()));
178
179            for neighbor in old_graph.graph.edges_directed(node, Direction::Outgoing) {
180                to_visit.push((Some((new_node, neighbor.weight())), neighbor.target()));
181            }
182
183            if let Some(parent) = parent {
184                new_graph.add_edge(parent.0, new_node, *parent.1);
185            } else {
186                new_root = Some(new_node);
187            }
188        }
189
190        match new_root {
191            Some(new_root) => {
192                let command_graph = CommandGraph {
193                    graph: new_graph,
194                    root: new_root,
195                };
196                let packet: CommandTreeS2c = command_graph.into();
197
198                client.write_packet(&packet);
199            }
200            None => {
201                warn!(
202                    "Client has no permissions to execute any commands so we sent them nothing. \
203                     It is generally a bad idea to scope the root node of the command graph as it \
204                     can cause undefined behavior. For example, if the player has permission to \
205                     execute a command before you change the scope of the root node, the packet \
206                     will not be sent to the client and so the client will still think they can \
207                     execute the command."
208                )
209            }
210        }
211
212        debug!("command tree update took {:?}", time.elapsed());
213    }
214}
215
216fn parse_incoming_commands(
217    mut event_reader: EventReader<CommandExecutionEvent>,
218    mut event_writer: EventWriter<CommandProcessedEvent>,
219    command_registry: Res<CommandRegistry>,
220    scope_registry: Res<CommandScopeRegistry>,
221    entity_scopes: Query<&CommandScopes>,
222) {
223    for command_event in event_reader.read() {
224        let executor = command_event.executor;
225        // these are the leafs of the graph that are executable under this command
226        // group
227        let executable_leafs = command_registry
228            .executables
229            .iter()
230            .collect::<Vec<&NodeIndex>>();
231        let root = command_registry.graph.root;
232
233        let command_input = &*command_event.command;
234        let graph = &command_registry.graph.graph;
235        let input = ParseInput::new(command_input);
236
237        let mut to_be_executed = Vec::new();
238
239        let mut args = Vec::new();
240        let mut modifiers_to_be_executed = Vec::new();
241
242        parse_command_args(
243            &mut args,
244            &mut modifiers_to_be_executed,
245            input,
246            graph,
247            &executable_leafs,
248            command_registry.as_ref(),
249            &mut to_be_executed,
250            root,
251            executor,
252            &entity_scopes,
253            scope_registry.as_ref(),
254            false,
255        );
256
257        let mut modifiers = HashMap::new();
258        for (node, modifier) in modifiers_to_be_executed {
259            command_registry.modifiers[&node](modifier, &mut modifiers);
260        }
261
262        for node in to_be_executed {
263            trace!("executing node: {node:?}");
264            event_writer.send(CommandProcessedEvent {
265                command: args.join(" "),
266                executor,
267                modifiers: modifiers.clone(),
268                node,
269            });
270        }
271        info!(
272            "Command dispatched: /{} (debug logs for more data)",
273            command_event.command
274        );
275        debug!("Command modifiers: {:?}", modifiers);
276    }
277}
278
279#[allow(clippy::too_many_arguments)]
280/// recursively parse the command args.
281fn parse_command_args(
282    command_args: &mut Vec<String>,
283    modifiers_to_be_executed: &mut Vec<(NodeIndex, String)>,
284    mut input: ParseInput,
285    graph: &Graph<CommandNode, CommandEdgeType>,
286    executable_leafs: &[&NodeIndex],
287    command_registry: &CommandRegistry,
288    to_be_executed: &mut Vec<NodeIndex>,
289    current_node: NodeIndex,
290    executor: Entity,
291    scopes: &Query<&CommandScopes>,
292    scope_registry: &CommandScopeRegistry,
293    coming_from_redirect: bool,
294) -> bool {
295    let node_scopes = &graph[current_node].scopes;
296    let default_scopes = CommandScopes::new();
297    let client_scopes: Vec<&str> = scopes
298        .get(executor)
299        .unwrap_or(&default_scopes)
300        .0
301        .iter()
302        .map(|scope| scope.as_str())
303        .collect();
304    // if empty, we assume the node is global
305    if !node_scopes.is_empty() {
306        let mut has_scope = false;
307        for scope in node_scopes {
308            if scope_registry.any_grants(&client_scopes, scope) {
309                has_scope = true;
310                break;
311            }
312        }
313        if !has_scope {
314            return false;
315        }
316    }
317
318    if !coming_from_redirect {
319        // we want to skip whitespace before matching the node
320        input.skip_whitespace();
321        match &graph[current_node].data {
322            // no real need to check for root node
323            NodeData::Root => {
324                if command_registry.modifiers.contains_key(&current_node) {
325                    modifiers_to_be_executed.push((current_node, String::new()));
326                }
327            }
328            // if the node is a literal, we want to match the name of the literal
329            // to the input
330            NodeData::Literal { name } => {
331                if input.match_next(name) {
332                    if !input.match_next(" ") && !input.is_done() {
333                        return false;
334                    } // we want to pop the whitespace after the literal
335                    if command_registry.modifiers.contains_key(&current_node) {
336                        modifiers_to_be_executed.push((current_node, String::new()));
337                    }
338                } else {
339                    return false;
340                }
341            }
342            // if the node is an argument, we want to parse the argument
343            NodeData::Argument { .. } => {
344                let Some(parser) = command_registry.parsers.get(&current_node) else {
345                    return false;
346                };
347
348                // we want to save the input before and after parsing
349                // this is so we can save the argument to the command args
350                let pre_input = input.clone().into_inner();
351                let valid = parser(&mut input);
352                if valid {
353                    // If input.len() > pre_input.len() the parser replaced the input
354                    let Some(arg) = pre_input
355                        .get(..pre_input.len().wrapping_sub(input.len()))
356                        .map(|s| s.to_owned())
357                    else {
358                        panic!(
359                            "Parser replaced input with another string. This is not allowed. \
360                             Attempting to parse: {}",
361                            input.into_inner()
362                        );
363                    };
364
365                    if command_registry.modifiers.contains_key(&current_node) {
366                        modifiers_to_be_executed.push((current_node, arg.clone()));
367                    }
368                    command_args.push(arg);
369                } else {
370                    return false;
371                }
372            }
373        }
374    } else {
375        command_args.clear();
376    }
377
378    input.skip_whitespace();
379    if input.is_done() && executable_leafs.contains(&&current_node) {
380        to_be_executed.push(current_node);
381        return true;
382    }
383
384    let mut all_invalid = true;
385    for neighbor in graph.neighbors(current_node) {
386        let pre_input = input.clone();
387        let mut args = command_args.clone();
388        let mut modifiers = modifiers_to_be_executed.clone();
389        let valid = parse_command_args(
390            &mut args,
391            &mut modifiers,
392            input.clone(),
393            graph,
394            executable_leafs,
395            command_registry,
396            to_be_executed,
397            neighbor,
398            executor,
399            scopes,
400            scope_registry,
401            {
402                let edge = graph.find_edge(current_node, neighbor).unwrap();
403                matches!(&graph[edge], CommandEdgeType::Redirect)
404            },
405        );
406        if valid {
407            *command_args = args;
408            *modifiers_to_be_executed = modifiers;
409            all_invalid = false;
410        } else {
411            input = pre_input;
412        }
413    }
414    if all_invalid {
415        return false;
416    }
417    true
418}