feat!: fix some lifetimes in InputContainerInner, almast done one example, add default for plugin, rebase src from crate/bevy_controls to src

This commit is contained in:
2024-02-10 02:42:18 +01:00
parent 5fc1382ceb
commit 608781faf6
19 changed files with 899 additions and 84 deletions
+105 -37
View File
@@ -1,102 +1,170 @@
use std::hash::Hash;
use bevy_ecs::{system::Resource, schedule::States};
use strum::IntoEnumIterator;
use bevy_ecs::{schedule::States, system::Resource};
#[cfg(feature = "inspector-egui")]
use bevy_inspector_egui::inspector_options::InspectorOptionsType;
use strum::IntoEnumIterator;
// #[cfg(all(feature = "reflect", feature = "serialize"))]
// use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
#[cfg(feature = "serialize")]
use serde::{Serialize, de::DeserializeOwned};
#[cfg(feature = "reflect")]
use bevy_reflect::{Reflect, TypePath, FromReflect};
use bevy_reflect::{FromReflect, Reflect, TypePath};
#[cfg(feature = "serialize")]
use serde::{de::DeserializeOwned, Serialize};
use crate::resource::PlayerInputs;
pub trait ActionInner: 'static + std::marker::Sync + std::marker::Send + PartialEq + Eq + Hash + IntoEnumIterator + Clone + Copy { }
pub trait ActionInner:
'static
+ std::marker::Sync
+ std::marker::Send
+ PartialEq
+ Eq
+ Hash
+ IntoEnumIterator
+ Clone
+ Copy
{
}
pub trait GameStateInner: 'static + std::marker::Sync + std::marker::Send + States { }
pub trait GameStateInner: 'static + std::marker::Sync + std::marker::Send + States + Default {}
pub trait InputsContainerInner<A: Action>: 'static + std::marker::Sync + std::marker::Send + Resource {
pub trait InputsContainerInner<A: Action>:
'static + std::marker::Sync + std::marker::Send + Resource + Default
{
/// The method returning an iterator over references to the PlayerInputs.
/// Note: the use of 'a for both the method's lifetime and the returned Iterator's lifetime.
fn iter_inputs<'a>(&'a self) -> Box<dyn Iterator<Item = &'a PlayerInputs<A>> + 'a>;
/// The method returning an player inputs of this client.
fn me<'a>(&self) -> Option<&'a PlayerInputs<A>>;
fn me<'a>(&'a self) -> Option<&'a PlayerInputs<A>>;
/// The method returning an mutable player inputs of this client.
fn me_mut<'a>(&self) -> Option<&'a mut PlayerInputs<A>>;
fn me_mut<'a>(&'a mut self) -> Option<&'a mut PlayerInputs<A>>;
}
#[cfg(feature = "serialize")]
pub trait SerializeImpl: Serialize + DeserializeOwned { }
pub trait SerializeImpl: Serialize + DeserializeOwned {}
#[cfg(feature = "reflect")]
pub trait ReflectImpl: Reflect + TypePath + FromReflect { }
pub trait ReflectImpl: Reflect + TypePath + FromReflect {}
#[cfg(feature = "inspector-egui")]
pub trait InspectorEguiImpl: InspectorOptionsType { }
pub trait InspectorEguiImpl: InspectorOptionsType {}
// ===== Action =====
// SAFATY: inspector-egui includes reflect
#[cfg(all(not(feature = "serialize"), not(feature = "reflect"), not(feature = "inspector-egui")))]
#[cfg(all(
not(feature = "serialize"),
not(feature = "reflect"),
not(feature = "inspector-egui")
))]
pub trait Action: ActionInner {}
#[cfg(all(feature = "serialize", not(feature = "reflect"), not(feature = "inspector-egui")))]
#[cfg(all(
feature = "serialize",
not(feature = "reflect"),
not(feature = "inspector-egui")
))]
pub trait Action: ActionInner + SerializeImpl {}
#[cfg(all(not(feature = "serialize"), feature = "reflect", not(feature = "inspector-egui")))]
#[cfg(all(
not(feature = "serialize"),
feature = "reflect",
not(feature = "inspector-egui")
))]
pub trait Action: ActionInner + ReflectImpl {}
#[cfg(all(feature = "serialize", feature = "reflect", not(feature = "inspector-egui")))]
pub trait Action: ActionInner + SerializeImpl + ReflectImpl{}
#[cfg(all(
feature = "serialize",
feature = "reflect",
not(feature = "inspector-egui")
))]
pub trait Action: ActionInner + SerializeImpl + ReflectImpl {}
#[cfg(all(not(feature = "serialize"), feature = "inspector-egui"))]
pub trait Action: ActionInner + ReflectImpl + InspectorEguiImpl {}
pub trait Action: ActionInner + ReflectImpl + InspectorEguiImpl {}
#[cfg(all(feature = "serialize", feature = "inspector-egui"))]
pub trait Action: ActionInner + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
pub trait Action: ActionInner + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
// ===== GameState =====
// SAFATY: inspector-egui includes reflect
#[cfg(all(not(feature = "serialize"), not(feature = "reflect"), not(feature = "inspector-egui")))]
#[cfg(all(
not(feature = "serialize"),
not(feature = "reflect"),
not(feature = "inspector-egui")
))]
pub trait GameState: GameStateInner {}
#[cfg(all(feature = "serialize", not(feature = "reflect"), not(feature = "inspector-egui")))]
#[cfg(all(
feature = "serialize",
not(feature = "reflect"),
not(feature = "inspector-egui")
))]
pub trait GameState: GameStateInner + SerializeImpl {}
#[cfg(all(not(feature = "serialize"), feature = "reflect", not(feature = "inspector-egui")))]
#[cfg(all(
not(feature = "serialize"),
feature = "reflect",
not(feature = "inspector-egui")
))]
pub trait GameState: GameStateInner + ReflectImpl {}
#[cfg(all(feature = "serialize", feature = "reflect", not(feature = "inspector-egui")))]
pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl{}
#[cfg(all(
feature = "serialize",
feature = "reflect",
not(feature = "inspector-egui")
))]
pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl {}
#[cfg(all(not(feature = "serialize"), feature = "inspector-egui"))]
pub trait GameState: GameStateInner + ReflectImpl + InspectorEguiImpl {}
pub trait GameState: GameStateInner + ReflectImpl + InspectorEguiImpl {}
#[cfg(all(feature = "serialize", feature = "inspector-egui"))]
pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
// ===== InputsContainer =====
// SAFATY: inspector-egui includes reflect
#[cfg(all(not(feature = "serialize"), not(feature = "reflect"), not(feature = "inspector-egui")))]
#[cfg(all(
not(feature = "serialize"),
not(feature = "reflect"),
not(feature = "inspector-egui")
))]
pub trait InputsContainer<A: Action>: InputsContainerInner<A> {}
#[cfg(all(feature = "serialize", not(feature = "reflect"), not(feature = "inspector-egui")))]
#[cfg(all(
feature = "serialize",
not(feature = "reflect"),
not(feature = "inspector-egui")
))]
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + SerializeImpl {}
#[cfg(all(not(feature = "serialize"), feature = "reflect", not(feature = "inspector-egui")))]
#[cfg(all(
not(feature = "serialize"),
feature = "reflect",
not(feature = "inspector-egui")
))]
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + ReflectImpl {}
#[cfg(all(feature = "serialize", feature = "reflect", not(feature = "inspector-egui")))]
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + SerializeImpl + ReflectImpl{}
#[cfg(all(
feature = "serialize",
feature = "reflect",
not(feature = "inspector-egui")
))]
pub trait InputsContainer<A: Action>:
InputsContainerInner<A> + SerializeImpl + ReflectImpl
{
}
#[cfg(all(not(feature = "serialize"), feature = "inspector-egui"))]
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + ReflectImpl + InspectorEguiImpl {}
pub trait InputsContainer<A: Action>:
InputsContainerInner<A> + ReflectImpl + InspectorEguiImpl
{
}
#[cfg(all(feature = "serialize", feature = "inspector-egui"))]
pub trait InputsContainer<A: Action>: InputsContainerInner<A> + SerializeImpl + ReflectImpl + InspectorEguiImpl {}
pub trait InputsContainer<A: Action>:
InputsContainerInner<A> + SerializeImpl + ReflectImpl + InspectorEguiImpl
{
}
// #[cfg(test)]
// mod test {
+18 -6
View File
@@ -4,10 +4,20 @@ use bevy_input::{Input, keyboard::KeyCode, mouse::MouseButton};
use crate::{resource::*, contract::{Action, GameState, InputsContainer}};
pub struct ControlsPlugin<Action, InputsContainer, GameState> {
_action: std::marker::PhantomData<Action>,
_inputs_container: std::marker::PhantomData<InputsContainer>,
_game_state: std::marker::PhantomData<GameState>,
pub struct ControlsPlugin<A, Ic, Gs> {
_action: std::marker::PhantomData<A>,
_inputs_container: std::marker::PhantomData<Ic>,
_game_state: std::marker::PhantomData<Gs>,
}
impl<A, Ic, Gs> Default for ControlsPlugin<A, Ic, Gs> {
fn default() -> Self {
Self {
_action: std::marker::PhantomData,
_inputs_container: std::marker::PhantomData,
_game_state: std::marker::PhantomData,
}
}
}
impl<
@@ -17,6 +27,8 @@ impl<
> Plugin for ControlsPlugin<A, Ic, Gs> {
fn build(&self, app: &mut App) {
app.init_resource::<Controls<A, Gs>>()
.init_resource::<Ic>()
.add_state::<Gs>()
.add_systems(Update, Self::save_input);
#[cfg(feature = "reflect")]
@@ -34,7 +46,7 @@ impl<
fn save_input(
keyboard_input: Res<Input<KeyCode>>,
mouse_input: Res<Input<MouseButton>>,
lobby: ResMut<Ic>,
mut lobby: ResMut<Ic>,
controls: Res<Controls<A, Gs>>,
game_state: Res<State<Gs>>,
) {
@@ -235,4 +247,4 @@ impl<
// log::info!("without casting: {:?}", duration);
// }
// }
// }
+30 -7
View File
@@ -80,6 +80,14 @@ impl<A: Action> PlayerInputs<A> {
*self.current.get(&action).unwrap()
}
// TODO
// /// Returns current input value for action
// ///
// /// in any case faster that `get_many` method
// pub fn all_active(&self) -> InputValue {
// *self.current.iter().filter(|a| {a.})
// }
/// Returns current input values for actions
pub fn get_many(&self, actions: Vec<A>) -> Vec<InputValue> {
// SAFETY: action is always valid
@@ -119,6 +127,8 @@ impl<A: Action> PlayerInputs<A> {
}
/// Set input value to new
///
/// notice: not recomended to use if you do not know what do you do
pub fn forced_set(&mut self, action: A, value: impl Into<InputValue>) {
// SAFETY: action is always valid
// because we iterate over all actions in `default` method
@@ -129,6 +139,8 @@ impl<A: Action> PlayerInputs<A> {
/// Set input value to new
/// if it is not the same InputValue type return `Error()`
///
/// notice: not recomended to use if you do not know what do you do
pub fn set(
&mut self,
action: A,
@@ -150,6 +162,8 @@ impl<A: Action> PlayerInputs<A> {
}
/// Update current inputs
///
/// notice: not recomended to use if you do not know what do you do
pub fn forced_update(&mut self, inputs: Inputs<A>) {
*self.previous = self.current.clone().0;
*self.current = inputs.0;
@@ -158,6 +172,8 @@ impl<A: Action> PlayerInputs<A> {
/// Update current inputs
/// if any input is not the same InputValue type return `Error()`
/// ! remember that it work safely but slow
///
/// notice: not recomended to use if you do not know what do you do
pub fn update(&mut self, inputs: Inputs<A>) -> Result<(), Box<dyn error::Error>> {
// SAFETY: action is always valid
// because we iterate over all actions in `default` method
@@ -222,6 +238,7 @@ common_traits_conditions! {
Tap,
}
#[derive(Clone)]
pub struct ActivationOptions {
/// Manner in which input is registered
pub mode: ActivationMode,
@@ -361,7 +378,7 @@ impl InputValue {
matches!(self, InputValue::Float(_))
}
pub fn as_boolean(&self) -> bool {
pub fn to_boolean(&self) -> bool {
match self {
InputValue::Boolean(value) => *value,
InputValue::Float(value) => *value > 0.0,
@@ -371,6 +388,7 @@ impl InputValue {
}
common_traits_conditions! {
#[derive(Clone)]
pub struct Bindings<Gs: GameState> {
/// List of bindings for action
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
@@ -450,7 +468,7 @@ impl<Gs: GameState> Bindings<Gs> {
common_traits_conditions! {
/// Contains all bindings for action
/// and different activation options
#[derive(Default)]
#[derive(Default, Clone)]
pub struct BindingConfig<Gs: GameState> {
#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))]
pub bindings: Bindings<Gs>,
@@ -470,22 +488,26 @@ impl<Gs: GameState> BindingConfig<Gs> {
common_traits_conditions! {
/// Contains all bindings for actions
#[derive(Resource)]
#[derive(Resource, Clone)]
#[cfg_attr(feature = "reflect", reflect(Resource))]
// TODO: Add delay for input
pub struct Controls<A: Action, Gs: GameState>(#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] HashMap<A, BindingConfig<Gs>>); // TODO: change HashMap to Vec for faster iteration
pub struct Controls<A: Action, Gs: GameState>(#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] HashMap<A, BindingConfig<Gs>>); // TODO: change HashMap to Vec for faster iteration
}
impl<A: Action, Gs: GameState> Default for Controls<A, Gs> {
fn default() -> Self {
// TODO: default must create empty controls or fill empty / default BindingConfig on any actions
let controls = Self(HashMap::new());
let mut controls = HashMap::new();
for key in A::iter() {
controls.insert(key, BindingConfig::default());
}
// If you see this error, you may add new action in menu_actions
// or make sure that you have only one Menufor<'a> Action<'a> with the same name in the Menufor<'a> Action<'a>s
assert!(validate_hash_map(&controls.0));
assert!(validate_hash_map(&controls));
controls
Self(controls)
}
}
@@ -498,6 +520,7 @@ impl<A: Action, Gs: GameState> Controls<A, Gs> {
/// Push new binding for action
pub fn push(&mut self, action: A, binding: Binding<Gs>) {
// TODO: warning if config is not exist yet
// TODO: interface for [`ActivationOptions`]
self.0
.entry(action)
.or_insert_with(BindingConfig::default)