leafwing_input_manager/
plugin.rs1use core::hash::Hash;
4use core::marker::PhantomData;
5use std::fmt::Debug;
6
7use bevy::app::{App, FixedPostUpdate, Plugin, RunFixedMainLoop};
8use bevy::input::InputSystems;
9#[cfg(feature = "picking")]
10use bevy::picking::PickingSystems;
11use bevy::prelude::*;
12use bevy::reflect::TypePath;
13#[cfg(feature = "ui")]
14use bevy::ui::UiSystems;
15use updating::CentralInputStore;
16
17use crate::action_state::{ActionState, ButtonData};
18use crate::clashing_inputs::ClashStrategy;
19use crate::input_map::InputMap;
20use crate::input_processing::*;
21use crate::prelude::updating::register_standard_input_kinds;
22#[cfg(feature = "timing")]
23use crate::timing::Timing;
24use crate::user_input::*;
25use crate::Actionlike;
26
27pub struct InputManagerPlugin<A: Actionlike> {
57 _phantom: PhantomData<A>,
58 machine: Machine,
59}
60
61impl<A: Actionlike> Default for InputManagerPlugin<A> {
63 fn default() -> Self {
64 Self {
65 _phantom: PhantomData,
66 machine: Machine::Client,
67 }
68 }
69}
70
71impl<A: Actionlike> InputManagerPlugin<A> {
72 #[must_use]
78 pub fn server() -> Self {
79 Self {
80 _phantom: PhantomData,
81 machine: Machine::Server,
82 }
83 }
84}
85
86enum Machine {
88 Server,
89 Client,
90}
91
92impl<A: Actionlike + TypePath + bevy::reflect::GetTypeRegistration> Plugin
93 for InputManagerPlugin<A>
94{
95 fn build(&self, app: &mut App) {
96 use crate::systems::*;
97
98 match self.machine {
99 Machine::Client => {
100 if !app.is_plugin_added::<CentralInputStorePlugin>() {
101 app.add_plugins(CentralInputStorePlugin);
102 }
103
104 app.add_systems(
106 PreUpdate,
107 (
108 tick_action_state::<A>.in_set(TickActionStateSystem::<A>::new()),
109 clear_central_input_store,
110 )
111 .in_set(InputManagerSystem::Tick)
112 .before(InputManagerSystem::Update),
113 )
114 .add_systems(PostUpdate, release_on_input_map_removed::<A>);
115
116 app.add_systems(
117 PreUpdate,
118 update_action_state::<A>.in_set(InputManagerSystem::Update),
119 );
120
121 app.configure_sets(
122 PreUpdate,
123 InputManagerSystem::ManualControl.after(InputManagerSystem::Update),
124 );
125
126 app.configure_sets(
127 PreUpdate,
128 InputManagerSystem::Unify
129 .after(InputManagerSystem::Filter)
130 .after(InputSystems),
131 );
132
133 app.configure_sets(
134 PreUpdate,
135 InputManagerSystem::Update
136 .after(InputSystems)
137 .after(InputManagerSystem::Unify),
138 );
139
140 #[cfg(feature = "ui")]
141 app.add_systems(
142 PreUpdate,
143 filter_captured_input
144 .before(update_action_state::<A>)
145 .in_set(InputManagerSystem::Filter),
146 );
147
148 #[cfg(feature = "ui")]
149 app.configure_sets(
150 PreUpdate,
151 InputManagerSystem::Filter.after(UiSystems::Focus),
152 );
153
154 #[cfg(feature = "ui")]
155 app.configure_sets(
156 PreUpdate,
157 InputManagerSystem::ManualControl
158 .after(InputManagerSystem::Tick)
159 .after(InputManagerSystem::Update)
162 .after(UiSystems::Focus)
163 .after(InputSystems),
164 );
165
166 #[cfg(feature = "picking")]
167 app.configure_sets(
168 PreUpdate,
169 InputManagerSystem::Update.before(PickingSystems::Hover),
170 );
171
172 app.add_systems(
174 RunFixedMainLoop,
175 (
176 swap_to_fixed_update::<A>,
177 update_action_state::<A>,
179 )
180 .chain()
181 .in_set(RunFixedMainLoopSystems::BeforeFixedMainLoop),
182 );
183
184 app.add_systems(FixedPostUpdate, release_on_input_map_removed::<A>);
185 app.add_systems(
186 FixedPostUpdate,
187 tick_action_state::<A>
188 .in_set(TickActionStateSystem::<A>::new())
189 .in_set(InputManagerSystem::Tick)
190 .before(InputManagerSystem::Update),
191 );
192 app.add_systems(
193 RunFixedMainLoop,
194 swap_to_update::<A>.in_set(RunFixedMainLoopSystems::AfterFixedMainLoop),
195 );
196 }
197 Machine::Server => {
198 app.add_systems(
199 PreUpdate,
200 tick_action_state::<A>
201 .in_set(TickActionStateSystem::<A>::new())
202 .in_set(InputManagerSystem::Tick),
203 );
204 }
205 };
206
207 #[cfg(feature = "mouse")]
208 app.register_buttonlike_input::<MouseButton>()
209 .register_buttonlike_input::<MouseMoveDirection>()
210 .register_buttonlike_input::<MouseButton>()
211 .register_axislike_input::<MouseMoveAxis>()
212 .register_dual_axislike_input::<MouseMove>()
213 .register_buttonlike_input::<MouseScrollDirection>()
214 .register_axislike_input::<MouseScrollAxis>()
215 .register_dual_axislike_input::<MouseScroll>();
216
217 #[cfg(feature = "keyboard")]
218 app.register_buttonlike_input::<KeyCode>()
219 .register_buttonlike_input::<ModifierKey>();
220
221 #[cfg(feature = "gamepad")]
222 app.register_buttonlike_input::<GamepadControlDirection>()
223 .register_axislike_input::<GamepadControlAxis>()
224 .register_dual_axislike_input::<GamepadStick>()
225 .register_buttonlike_input::<GamepadButton>();
226
227 app.register_axislike_input::<VirtualAxis>()
229 .register_dual_axislike_input::<VirtualDPad>()
230 .register_triple_axislike_input::<VirtualDPad3D>();
231
232 app.register_buttonlike_input::<ButtonlikeChord>()
234 .register_axislike_input::<AxislikeChord>()
235 .register_dual_axislike_input::<DualAxislikeChord>()
236 .register_triple_axislike_input::<TripleAxislikeChord>();
237
238 app.register_type::<ActionState<A>>()
240 .register_type::<InputMap<A>>()
241 .register_type::<ButtonData>()
242 .register_type::<ActionState<A>>()
243 .register_type::<CentralInputStore>();
244
245 app.register_type::<AxisProcessor>()
247 .register_type::<AxisBounds>()
248 .register_type::<AxisExclusion>()
249 .register_type::<AxisDeadZone>()
250 .register_type::<DualAxisProcessor>()
251 .register_type::<DualAxisInverted>()
252 .register_type::<DualAxisSensitivity>()
253 .register_type::<DualAxisBounds>()
254 .register_type::<DualAxisExclusion>()
255 .register_type::<DualAxisDeadZone>()
256 .register_type::<CircleBounds>()
257 .register_type::<CircleExclusion>()
258 .register_type::<CircleDeadZone>();
259
260 app.init_resource::<ClashStrategy>();
262
263 #[cfg(feature = "timing")]
264 app.register_type::<Timing>();
265 }
266}
267
268#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
272pub enum InputManagerSystem {
273 Tick,
277 Accumulate,
279 Filter,
281 Unify,
283 Update,
287 ManualControl,
291}
292
293#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
294pub struct TickActionStateSystem<A: Actionlike> {
296 phantom_data: PhantomData<A>,
297}
298
299impl<A: Actionlike> TickActionStateSystem<A> {
300 pub fn new() -> Self {
302 Self {
303 phantom_data: PhantomData,
304 }
305 }
306}
307
308impl<A: Actionlike> Default for TickActionStateSystem<A> {
309 fn default() -> Self {
310 Self::new()
311 }
312}
313
314pub struct CentralInputStorePlugin;
321
322impl Plugin for CentralInputStorePlugin {
323 fn build(&self, app: &mut App) {
324 app.insert_resource(CentralInputStore::default());
325
326 register_standard_input_kinds(app);
327
328 app.configure_sets(PreUpdate, InputManagerSystem::Unify.after(InputSystems));
329 }
330}