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#[derive(Debug, Clone, PartialEq, Eq, Hash, Event)]
60pub struct CommandExecutionEvent {
61 pub command: String,
63 pub executor: Entity,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Event)]
71pub struct CommandProcessedEvent {
72 pub command: String,
74 pub executor: Entity,
77 pub modifiers: HashMap<ModifierValue, ModifierValue>,
79 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 let root = old_graph.root;
147
148 let mut to_visit = vec![(None, root)];
149 let mut already_visited = HashSet::new(); 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 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)]
280fn 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 !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 input.skip_whitespace();
321 match &graph[current_node].data {
322 NodeData::Root => {
324 if command_registry.modifiers.contains_key(¤t_node) {
325 modifiers_to_be_executed.push((current_node, String::new()));
326 }
327 }
328 NodeData::Literal { name } => {
331 if input.match_next(name) {
332 if !input.match_next(" ") && !input.is_done() {
333 return false;
334 } if command_registry.modifiers.contains_key(¤t_node) {
336 modifiers_to_be_executed.push((current_node, String::new()));
337 }
338 } else {
339 return false;
340 }
341 }
342 NodeData::Argument { .. } => {
344 let Some(parser) = command_registry.parsers.get(¤t_node) else {
345 return false;
346 };
347
348 let pre_input = input.clone().into_inner();
351 let valid = parser(&mut input);
352 if valid {
353 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(¤t_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(&¤t_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}