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