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
108fn 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}