use core::hash::Hash;
use core::marker::PhantomData;
use std::fmt::Debug;
use bevy::app::{App, FixedPostUpdate, Plugin, RunFixedMainLoop};
use bevy::input::InputSystems;
#[cfg(feature = "picking")]
use bevy::picking::PickingSystems;
use bevy::prelude::*;
use bevy::reflect::TypePath;
#[cfg(feature = "ui")]
use bevy::ui::UiSystems;
use updating::CentralInputStore;
use crate::Actionlike;
use crate::action_state::{ActionState, ButtonData};
use crate::clashing_inputs::ClashStrategy;
use crate::input_map::InputMap;
use crate::input_processing::*;
use crate::prelude::updating::register_standard_input_kinds;
#[cfg(feature = "timing")]
use crate::timing::Timing;
use crate::user_input::*;
pub struct InputManagerPlugin<A: Actionlike> {
_phantom: PhantomData<A>,
machine: Machine,
}
impl<A: Actionlike> Default for InputManagerPlugin<A> {
fn default() -> Self {
Self {
_phantom: PhantomData,
machine: Machine::Client,
}
}
}
impl<A: Actionlike> InputManagerPlugin<A> {
#[must_use]
pub fn server() -> Self {
Self {
_phantom: PhantomData,
machine: Machine::Server,
}
}
}
enum Machine {
Server,
Client,
}
impl<A: Actionlike + TypePath + bevy::reflect::GetTypeRegistration> Plugin
for InputManagerPlugin<A>
{
fn build(&self, app: &mut App) {
use crate::systems::*;
match self.machine {
Machine::Client => {
if !app.is_plugin_added::<CentralInputStorePlugin>() {
app.add_plugins(CentralInputStorePlugin);
}
app.add_systems(
PreUpdate,
(
tick_action_state::<A>.in_set(TickActionStateSystem::<A>::new()),
clear_central_input_store,
)
.in_set(InputManagerSystem::Tick)
.before(InputManagerSystem::Update),
)
.add_systems(PostUpdate, release_on_input_map_removed::<A>);
app.add_systems(
PreUpdate,
update_action_state::<A>.in_set(InputManagerSystem::Update),
);
app.configure_sets(
PreUpdate,
InputManagerSystem::ManualControl.after(InputManagerSystem::Update),
);
app.configure_sets(
PreUpdate,
InputManagerSystem::Unify
.after(InputManagerSystem::Filter)
.after(InputSystems),
);
app.configure_sets(
PreUpdate,
InputManagerSystem::Update
.after(InputSystems)
.after(InputManagerSystem::Unify),
);
#[cfg(feature = "ui")]
app.add_systems(
PreUpdate,
filter_captured_input
.before(update_action_state::<A>)
.in_set(InputManagerSystem::Filter),
);
#[cfg(feature = "ui")]
app.configure_sets(
PreUpdate,
InputManagerSystem::Filter.after(UiSystems::Focus),
);
#[cfg(feature = "ui")]
app.configure_sets(
PreUpdate,
InputManagerSystem::ManualControl
.after(InputManagerSystem::Tick)
.after(InputManagerSystem::Update)
.after(UiSystems::Focus)
.after(InputSystems),
);
#[cfg(feature = "picking")]
app.configure_sets(
PreUpdate,
InputManagerSystem::Update.before(PickingSystems::Hover),
);
app.add_systems(
RunFixedMainLoop,
(
swap_to_fixed_update::<A>,
update_action_state::<A>,
)
.chain()
.in_set(RunFixedMainLoopSystems::BeforeFixedMainLoop),
);
app.add_systems(FixedPostUpdate, release_on_input_map_removed::<A>);
app.add_systems(
FixedPostUpdate,
tick_action_state::<A>
.in_set(TickActionStateSystem::<A>::new())
.in_set(InputManagerSystem::Tick)
.before(InputManagerSystem::Update),
);
app.add_systems(
RunFixedMainLoop,
swap_to_update::<A>.in_set(RunFixedMainLoopSystems::AfterFixedMainLoop),
);
}
Machine::Server => {
app.add_systems(
PreUpdate,
tick_action_state::<A>
.in_set(TickActionStateSystem::<A>::new())
.in_set(InputManagerSystem::Tick),
);
}
};
#[cfg(feature = "mouse")]
app.register_buttonlike_input::<MouseButton>()
.register_buttonlike_input::<MouseMoveDirection>()
.register_buttonlike_input::<MouseButton>()
.register_axislike_input::<MouseMoveAxis>()
.register_dual_axislike_input::<MouseMove>()
.register_buttonlike_input::<MouseScrollDirection>()
.register_axislike_input::<MouseScrollAxis>()
.register_dual_axislike_input::<MouseScroll>();
#[cfg(feature = "keyboard")]
app.register_buttonlike_input::<KeyCode>()
.register_buttonlike_input::<ModifierKey>();
#[cfg(feature = "gamepad")]
app.register_buttonlike_input::<GamepadControlDirection>()
.register_axislike_input::<GamepadControlAxis>()
.register_dual_axislike_input::<GamepadStick>()
.register_buttonlike_input::<GamepadButton>();
app.register_axislike_input::<VirtualAxis>()
.register_dual_axislike_input::<VirtualDPad>()
.register_triple_axislike_input::<VirtualDPad3D>();
app.register_buttonlike_input::<ButtonlikeChord>()
.register_axislike_input::<AxislikeChord>()
.register_dual_axislike_input::<DualAxislikeChord>()
.register_triple_axislike_input::<TripleAxislikeChord>();
app.register_type::<ActionState<A>>()
.register_type::<InputMap<A>>()
.register_type::<ButtonData>()
.register_type::<ActionState<A>>()
.register_type::<CentralInputStore>();
app.register_type::<AxisProcessor>()
.register_type::<AxisBounds>()
.register_type::<AxisExclusion>()
.register_type::<AxisDeadZone>()
.register_type::<DualAxisProcessor>()
.register_type::<DualAxisInverted>()
.register_type::<DualAxisSensitivity>()
.register_type::<DualAxisBounds>()
.register_type::<DualAxisExclusion>()
.register_type::<DualAxisDeadZone>()
.register_type::<CircleBounds>()
.register_type::<CircleExclusion>()
.register_type::<CircleDeadZone>();
app.init_resource::<ClashStrategy>();
#[cfg(feature = "timing")]
app.register_type::<Timing>();
}
}
#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
pub enum InputManagerSystem {
Tick,
Accumulate,
Filter,
Unify,
Update,
ManualControl,
}
#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
pub struct TickActionStateSystem<A: Actionlike> {
phantom_data: PhantomData<A>,
}
impl<A: Actionlike> TickActionStateSystem<A> {
pub fn new() -> Self {
Self {
phantom_data: PhantomData,
}
}
}
impl<A: Actionlike> Default for TickActionStateSystem<A> {
fn default() -> Self {
Self::new()
}
}
pub struct CentralInputStorePlugin;
impl Plugin for CentralInputStorePlugin {
fn build(&self, app: &mut App) {
app.insert_resource(CentralInputStore::default());
register_standard_input_kinds(app);
app.configure_sets(PreUpdate, InputManagerSystem::Unify.after(InputSystems));
}
}