valence_command/
handler.rs

1use std::collections::HashMap;
2use std::marker::PhantomData;
3
4use bevy_app::{App, Plugin, PostStartup};
5use bevy_ecs::change_detection::ResMut;
6use bevy_ecs::event::{Event, EventReader, EventWriter};
7use bevy_ecs::prelude::{Entity, IntoSystemConfigs, Resource};
8use petgraph::prelude::NodeIndex;
9use valence_server::EventLoopPreUpdate;
10
11use crate::graph::CommandGraphBuilder;
12use crate::modifier_value::ModifierValue;
13use crate::parsers::ParseInput;
14use crate::{
15    Command, CommandProcessedEvent, CommandRegistry, CommandScopeRegistry, CommandSystemSet,
16};
17
18impl<T> Plugin for CommandHandlerPlugin<T>
19where
20    T: Command + Send + Sync + 'static,
21{
22    fn build(&self, app: &mut App) {
23        app.add_event::<CommandResultEvent<T>>()
24            .insert_resource(CommandResource::<T>::new())
25            .add_systems(PostStartup, command_startup_system::<T>)
26            .add_systems(
27                EventLoopPreUpdate,
28                command_event_system::<T>.after(CommandSystemSet),
29            );
30    }
31}
32
33pub struct CommandHandlerPlugin<T>
34where
35    T: Command,
36{
37    command: PhantomData<T>,
38}
39
40impl<T: Command> Default for CommandHandlerPlugin<T> {
41    fn default() -> Self {
42        Self::new()
43    }
44}
45
46impl<T> CommandHandlerPlugin<T>
47where
48    T: Command,
49{
50    pub fn new() -> Self {
51        CommandHandlerPlugin {
52            command: PhantomData,
53        }
54    }
55}
56
57#[derive(Resource)]
58struct CommandResource<T: Command + Send + Sync> {
59    command: PhantomData<T>,
60    executables: HashMap<NodeIndex, fn(&mut ParseInput) -> T>,
61}
62
63impl<T: Command + Send + Sync> CommandResource<T> {
64    fn new() -> Self {
65        CommandResource {
66            command: PhantomData,
67            executables: HashMap::new(),
68        }
69    }
70}
71
72#[derive(Event)]
73pub struct CommandResultEvent<T>
74where
75    T: Command,
76    T: Send + Sync + 'static,
77{
78    pub result: T,
79    pub executor: Entity,
80    pub modifiers: HashMap<ModifierValue, ModifierValue>,
81}
82
83fn command_startup_system<T>(
84    mut registry: ResMut<CommandRegistry>,
85    mut scope_registry: ResMut<CommandScopeRegistry>,
86    mut command: ResMut<CommandResource<T>>,
87) where
88    T: Command + Send + Sync + 'static,
89{
90    let mut executables = HashMap::new();
91    let mut parsers = HashMap::new();
92    let mut modifiers = HashMap::new();
93    let graph_builder = &mut CommandGraphBuilder::new(
94        &mut registry,
95        &mut executables,
96        &mut parsers,
97        &mut modifiers,
98    );
99    T::assemble_graph(graph_builder);
100    graph_builder.apply_scopes(&mut scope_registry);
101
102    command.executables.extend(executables.clone());
103    registry.parsers.extend(parsers);
104    registry.modifiers.extend(modifiers);
105    registry.executables.extend(executables.keys());
106}
107
108/// This system reads incoming command events.
109fn command_event_system<T>(
110    mut commands_executed: EventReader<CommandProcessedEvent>,
111    mut events: EventWriter<CommandResultEvent<T>>,
112    command: ResMut<CommandResource<T>>,
113) where
114    T: Command + Send + Sync,
115{
116    for command_event in commands_executed.read() {
117        if let Some(executable) = command.executables.get(&command_event.node) {
118            let result = executable(&mut ParseInput::new(&command_event.command));
119            events.send(CommandResultEvent {
120                result,
121                executor: command_event.executor,
122                modifiers: command_event.modifiers.clone(),
123            });
124        }
125    }
126}