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::InputSystem;
9#[cfg(feature = "picking")]
10use bevy::picking::PickSet;
11use bevy::prelude::*;
12use bevy::reflect::TypePath;
13#[cfg(feature = "ui")]
14use bevy::ui::UiSystem;
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(InputSystem),
131 );
132
133 app.configure_sets(
134 PreUpdate,
135 InputManagerSystem::Update
136 .after(InputSystem)
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(PreUpdate, InputManagerSystem::Filter.after(UiSystem::Focus));
150
151 #[cfg(feature = "ui")]
152 app.configure_sets(
153 PreUpdate,
154 InputManagerSystem::ManualControl
155 .after(InputManagerSystem::Tick)
156 .after(InputManagerSystem::Update)
159 .after(UiSystem::Focus)
160 .after(InputSystem),
161 );
162
163 #[cfg(feature = "picking")]
164 app.configure_sets(PreUpdate, InputManagerSystem::Update.before(PickSet::Hover));
165
166 app.add_systems(
168 RunFixedMainLoop,
169 (
170 swap_to_fixed_update::<A>,
171 update_action_state::<A>,
173 )
174 .chain()
175 .in_set(RunFixedMainLoopSystem::BeforeFixedMainLoop),
176 );
177
178 app.add_systems(FixedPostUpdate, release_on_input_map_removed::<A>);
179 app.add_systems(
180 FixedPostUpdate,
181 tick_action_state::<A>
182 .in_set(TickActionStateSystem::<A>::new())
183 .in_set(InputManagerSystem::Tick)
184 .before(InputManagerSystem::Update),
185 );
186 app.add_systems(
187 RunFixedMainLoop,
188 swap_to_update::<A>.in_set(RunFixedMainLoopSystem::AfterFixedMainLoop),
189 );
190 }
191 Machine::Server => {
192 app.add_systems(
193 PreUpdate,
194 tick_action_state::<A>
195 .in_set(TickActionStateSystem::<A>::new())
196 .in_set(InputManagerSystem::Tick),
197 );
198 }
199 };
200
201 #[cfg(feature = "mouse")]
202 app.register_buttonlike_input::<MouseButton>()
203 .register_buttonlike_input::<MouseMoveDirection>()
204 .register_buttonlike_input::<MouseButton>()
205 .register_axislike_input::<MouseMoveAxis>()
206 .register_dual_axislike_input::<MouseMove>()
207 .register_buttonlike_input::<MouseScrollDirection>()
208 .register_axislike_input::<MouseScrollAxis>()
209 .register_dual_axislike_input::<MouseScroll>();
210
211 #[cfg(feature = "keyboard")]
212 app.register_buttonlike_input::<KeyCode>()
213 .register_buttonlike_input::<ModifierKey>();
214
215 #[cfg(feature = "gamepad")]
216 app.register_buttonlike_input::<GamepadControlDirection>()
217 .register_axislike_input::<GamepadControlAxis>()
218 .register_dual_axislike_input::<GamepadStick>()
219 .register_buttonlike_input::<GamepadButton>();
220
221 app.register_axislike_input::<VirtualAxis>()
223 .register_dual_axislike_input::<VirtualDPad>()
224 .register_triple_axislike_input::<VirtualDPad3D>();
225
226 app.register_buttonlike_input::<ButtonlikeChord>()
228 .register_axislike_input::<AxislikeChord>()
229 .register_dual_axislike_input::<DualAxislikeChord>()
230 .register_triple_axislike_input::<TripleAxislikeChord>();
231
232 app.register_type::<ActionState<A>>()
234 .register_type::<InputMap<A>>()
235 .register_type::<ButtonData>()
236 .register_type::<ActionState<A>>()
237 .register_type::<CentralInputStore>();
238
239 app.register_type::<AxisProcessor>()
241 .register_type::<AxisBounds>()
242 .register_type::<AxisExclusion>()
243 .register_type::<AxisDeadZone>()
244 .register_type::<DualAxisProcessor>()
245 .register_type::<DualAxisInverted>()
246 .register_type::<DualAxisSensitivity>()
247 .register_type::<DualAxisBounds>()
248 .register_type::<DualAxisExclusion>()
249 .register_type::<DualAxisDeadZone>()
250 .register_type::<CircleBounds>()
251 .register_type::<CircleExclusion>()
252 .register_type::<CircleDeadZone>();
253
254 app.init_resource::<ClashStrategy>();
256
257 #[cfg(feature = "timing")]
258 app.register_type::<Timing>();
259 }
260}
261
262#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
266pub enum InputManagerSystem {
267 Tick,
271 Accumulate,
273 Filter,
275 Unify,
277 Update,
281 ManualControl,
285}
286
287#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
288pub struct TickActionStateSystem<A: Actionlike> {
290 phantom_data: PhantomData<A>,
291}
292
293impl<A: Actionlike> TickActionStateSystem<A> {
294 pub fn new() -> Self {
296 Self {
297 phantom_data: PhantomData,
298 }
299 }
300}
301
302impl<A: Actionlike> Default for TickActionStateSystem<A> {
303 fn default() -> Self {
304 Self::new()
305 }
306}
307
308pub struct CentralInputStorePlugin;
315
316impl Plugin for CentralInputStorePlugin {
317 fn build(&self, app: &mut App) {
318 app.insert_resource(CentralInputStore::default());
319
320 register_standard_input_kinds(app);
321
322 app.configure_sets(PreUpdate, InputManagerSystem::Unify.after(InputSystem));
323 }
324}