diff --git a/crate/bevy_controls/src/contract.rs b/crate/bevy_controls/src/contract.rs index 692de83..082f82e 100644 --- a/crate/bevy_controls/src/contract.rs +++ b/crate/bevy_controls/src/contract.rs @@ -1,13 +1,77 @@ use std::hash::Hash; use bevy_ecs::{system::Resource, schedule::States}; -use serde::{Serialize, Deserialize}; +use serde::de; use strum::IntoEnumIterator; use strum_macros::EnumIter; +#[cfg(feature = "inspector-egui")] +use bevy_inspector_egui::prelude::*; +#[cfg(all(feature = "reflect", feature = "serialize"))] +use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; +#[cfg(feature = "serialize")] +use serde::{Serialize, Deserialize, de::DeserializeOwned}; +#[cfg(feature = "reflect")] +use {bevy_ecs::prelude::ReflectResource, bevy_reflect::Reflect}; use crate::resource::PlayerInputs; -pub trait InputsContainer: 'static + std::marker::Sync + std::marker::Send + Resource { +macro_rules! define_common_trait { + ($trait_name:ident, $trait_inner:ident) => { + #[cfg(all(not(feature = "serialize"), not(feature = "reflect"), not(feature = "inspector-egui")))] + pub trait $trait_name: $trait_inner {} + + #[cfg(all(feature = "serialize", not(feature = "reflect"), not(feature = "inspector-egui")))] + pub trait $trait_name: $trait_inner + Serialize + DeserializeOwned {} + + #[cfg(all(not(feature = "serialize"), feature = "reflect", not(feature = "inspector-egui")))] + pub trait $trait_name: $trait_inner + bevy_reflect::Reflect {} + + #[cfg(all(feature = "serialize", feature = "reflect", not(feature = "inspector-egui")))] + pub trait $trait_name: $trait_inner + bevy_reflect::Reflect + Serialize + DeserializeOwned {} + + #[cfg(all(not(feature = "serialize"), not(feature = "reflect"), feature = "inspector-egui"))] + pub trait $trait_name: $trait_inner + bevy_inspector_egui::Inspectable + InspectorOptions {} + + #[cfg(all(feature = "serialize", not(feature = "reflect"), feature = "inspector-egui"))] + pub trait $trait_name: $trait_inner + bevy_inspector_egui::Inspectable + Serialize + DeserializeOwned + InspectorOptions {} + + #[cfg(all(not(feature = "serialize"), feature = "reflect", feature = "inspector-egui"))] + pub trait $trait_name: $trait_inner + bevy_inspector_egui::Inspectable + bevy_reflect::Reflect + InspectorOptions {} + + #[cfg(all(feature = "serialize", feature = "reflect", feature = "inspector-egui"))] + pub trait $trait_name: $trait_inner + bevy_inspector_egui::Inspectable + bevy_reflect::Reflect + Serialize + DeserializeOwned + InspectorOptions {} + }; + ($trait_name:ident<$generic:ident : $bound:path>, $trait_inner:ident<$trait_inner_generic:ident>) => { + #[cfg(all(not(feature = "serialize"), not(feature = "reflect"), not(feature = "inspector-egui")))] + pub trait $trait_name<$generic: $bound>: $trait_inner<$trait_inner_generic> {} + + #[cfg(all(feature = "serialize", not(feature = "reflect"), not(feature = "inspector-egui")))] + pub trait $trait_name<$generic: $bound>: $trait_inner<$trait_inner_generic> + Serialize + DeserializeOwned {} + + #[cfg(all(not(feature = "serialize"), feature = "reflect", not(feature = "inspector-egui")))] + pub trait $trait_name<$generic: $bound>: $trait_inner<$trait_inner_generic> + bevy_reflect::Reflect {} + + #[cfg(all(feature = "serialize", feature = "reflect", not(feature = "inspector-egui")))] + pub trait $trait_name<$generic: $bound>: $trait_inner<$trait_inner_generic> + bevy_reflect::Reflect + Serialize + DeserializeOwned {} + + #[cfg(all(not(feature = "serialize"), not(feature = "reflect"), feature = "inspector-egui"))] + pub trait $trait_name<$generic: $bound>: $trait_inner<$trait_inner_generic> + bevy_inspector_egui::Inspectable + InspectorOptions {} + + #[cfg(all(feature = "serialize", not(feature = "reflect"), feature = "inspector-egui"))] + pub trait $trait_name<$generic: $bound>: $trait_inner<$trait_inner_generic> + bevy_inspector_egui::Inspectable + Serialize + DeserializeOwned + InspectorOptions {} + + #[cfg(all(not(feature = "serialize"), feature = "reflect", feature = "inspector-egui"))] + pub trait $trait_name<$generic: $bound>: $trait_inner<$trait_inner_generic> + bevy_inspector_egui::Inspectable + bevy_reflect::Reflect + InspectorOptions {} + + #[cfg(all(feature = "serialize", feature = "reflect", feature = "inspector-egui"))] + pub trait $trait_name<$generic: $bound>: $trait_inner<$trait_inner_generic> + bevy_inspector_egui::Inspectable + bevy_reflect::Reflect + Serialize + DeserializeOwned + InspectorOptions {} + }; +} +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 InputsContainerInner: 'static + std::marker::Sync + std::marker::Send + Resource { /// 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> + 'a>; @@ -17,93 +81,84 @@ pub trait InputsContainer: 'static + std::marker::Sync + std::marker: fn me_mut<'a>(&self) -> Option<&'a mut PlayerInputs>; } -#[cfg(all(not(feature = "serialize"), not(feature = "reflect"), not(feature = "inspector-egui")))] -pub trait Action: 'static + std::marker::Sync + std::marker::Send + PartialEq + Eq + Hash + IntoEnumIterator + Clone + Copy { } - -#[cfg(all(feature = "serialize", not(feature = "reflect"), not(feature = "inspector-egui")))] -pub trait Action<'a>: 'static + std::marker::Sync + std::marker::Send + PartialEq + Eq + Hash + IntoEnumIterator + Clone + Copy + Serialize + Deserialize<'a> { } - -#[cfg(all(not(feature = "serialize"), feature = "reflect", not(feature = "inspector-egui")))] -pub trait Action: 'static + std::marker::Sync + std::marker::Send + PartialEq + Eq + Hash + IntoEnumIterator + Clone + Copy + Serialize + Deserialize + Reflect { } +define_common_trait!(Action, ActionInner); +define_common_trait!(GameState, GameStateInner); +define_common_trait!(InputsContainer, InputsContainerInner); -pub trait GameState: 'static + std::marker::Sync + std::marker::Send + States { +// #[cfg(test)] +// mod test { +// use bevy_ecs::{entity::Entity, schedule::States}; +// use bevy_utils::{HashMap, prelude::default}; +// use strum_macros::EnumIter; -} +// #[cfg(feature = "serialize")] +// use serde::{Serialize, Deserialize}; -#[cfg(test)] -mod test { - use bevy_ecs::{entity::Entity, schedule::States}; - use bevy_utils::{HashMap, prelude::default}; - use strum_macros::EnumIter; +// use crate::{hashmap, resource::PlayerInputs, common_traits_conditions}; - #[cfg(feature = "serialize")] - use serde::{Serialize, Deserialize}; +// common_traits_conditions! { +// #[derive(Debug, Clone, PartialEq)] +// #[cfg_attr(feature = "reflect", reflect(Resource))] +// pub struct Lobby { +// pub me: PlayerId, +// pub players: HashMap, +// } +// } - use crate::{hashmap, resource::PlayerInputs, common_traits_conditions}; +// impl Default for Lobby { +// fn default() -> Self { +// Self { +// me: PlayerId::Host, +// players: hashmap! { +// PlayerId::Host => Player::default() +// }, +// } +// } +// } - common_traits_conditions! { - #[derive(Debug, Clone, PartialEq)] - #[cfg_attr(feature = "reflect", reflect(Resource))] - pub struct Lobby { - pub me: PlayerId, - pub players: HashMap, - } - } +// common_traits_conditions! { +// #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +// pub enum PlayerId { +// /// Host or alone +// Host, +// /// Client +// Client(()), +// } - impl Default for Lobby { - fn default() -> Self { - Self { - me: PlayerId::Host, - players: hashmap! { - PlayerId::Host => Player::default() - }, - } - } - } +// #[derive(Default, Debug, PartialEq, Clone)] +// pub struct Player { +// /// Client do not need to know about other clients +// #[cfg_attr(feature = "serialize", serde(skip))] +// pub inputs: PlayerInputs, +// #[cfg_attr(feature = "serialize", serde(skip))] +// pub entity: Option, +// } +// } - common_traits_conditions! { - #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] - pub enum PlayerId { - /// Host or alone - Host, - /// Client - Client(()), - } +// #[derive(Debug, PartialEq, Clone, States, Default, Hash, Eq)] +// pub enum GameState { +// #[default] +// LevelEditor, +// InGame, +// } - #[derive(Default, Debug, PartialEq, Clone)] - pub struct Player { - /// Client do not need to know about other clients - #[cfg_attr(feature = "serialize", serde(skip))] - pub inputs: PlayerInputs, - #[cfg_attr(feature = "serialize", serde(skip))] - pub entity: Option, - } - } +// impl super::GameState for GameState {} - #[derive(Debug, PartialEq, Clone, States, Default, Hash, Eq)] - pub enum GameState { - #[default] - LevelEditor, - InGame, - } +// // TODO: it is just plug, it must be a trait +// /// Actions that can be performed by player +// #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, EnumIter)] +// pub enum Action { +// /// Move forward +// LeverEditorForward, +// /// Move backward +// LevelEditorBackward, +// /// Move left +// LevelEditorLeft, +// /// Move right +// LevelEditorRight, +// LevelEditorFly, +// } - impl super::GameState for GameState {} - - // TODO: it is just plug, it must be a trait - /// Actions that can be performed by player - #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, EnumIter)] - pub enum Action { - /// Move forward - LeverEditorForward, - /// Move backward - LevelEditorBackward, - /// Move left - LevelEditorLeft, - /// Move right - LevelEditorRight, - LevelEditorFly, - } - - impl super::Action for Action {} -} +// impl super::Action for Action {} +// } diff --git a/crate/bevy_controls/src/resource.rs b/crate/bevy_controls/src/resource.rs index bb9fec9..88b6043 100644 --- a/crate/bevy_controls/src/resource.rs +++ b/crate/bevy_controls/src/resource.rs @@ -10,7 +10,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; use bevy_utils::HashMap; use log::warn; #[cfg(feature = "serialize")] -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, de::DeserializeOwned, Serialize}; use std::mem::discriminant; use strum::IntoEnumIterator; use strum_macros::EnumIter; @@ -39,15 +39,18 @@ where true } + common_traits_conditions! { /// Struct that contains user's inputs corresponding to actions #[derive(Debug, PartialEq, Clone, Deref, DerefMut)] - pub struct Inputs(HashMap); + pub struct Inputs(#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] HashMap); /// Resource that contains current user inputs #[derive(Debug, PartialEq, Clone)] pub struct PlayerInputs { + #[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] current: Inputs, + #[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] previous: Inputs, } } @@ -212,9 +215,9 @@ common_traits_conditions! { /// Represents the manner in which input is registered #[derive(Debug, PartialEq, Clone)] pub enum ActivationMode { - /// Action is triggered when the button is pressed + /// for<'a> Action<'a> is triggered when the button is pressed Hold, - /// Action is triggered when the button is pressed + /// for<'a> Action<'a> is triggered when the button is pressed /// and cannot be triggered again until the button is released /// you don't need to release all buttons in chord /// you can release only last pressed button @@ -269,6 +272,7 @@ common_traits_conditions! { #[derive(Debug, PartialEq, Clone)] pub enum BindingCondition { /// During specific game state + #[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] InGameState(Gs), /// Binding is active only if player is in pause menu DuringPauseMenu(bool), @@ -280,6 +284,7 @@ common_traits_conditions! { #[derive(Debug, PartialEq, Clone)] pub struct Binding { pub input: ButtonCombination, + #[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] pub conditions: Vec>, } } @@ -373,6 +378,7 @@ impl InputValue { common_traits_conditions! { pub struct Bindings { /// List of bindings for action + #[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] list: Vec>, /// Possibility to change this binding options: OptionsMode, @@ -451,6 +457,7 @@ common_traits_conditions! { /// and different activation options #[derive(Default)] pub struct BindingConfig { + #[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] pub bindings: Bindings, #[cfg_attr(feature = "serialize", serde(skip))] pub activation: ActivationOptions, @@ -471,7 +478,7 @@ common_traits_conditions! { #[derive(Resource)] #[cfg_attr(feature = "reflect", reflect(Resource))] // TODO: Add delay for input - pub struct Controls(HashMap>); // TODO: change HashMap to Vec for faster iteration + pub struct Controls(#[cfg_attr(feature = "serialize", serde(bound(deserialize = "")))] HashMap>); // TODO: change HashMap to Vec for faster iteration } impl Default for Controls { @@ -480,7 +487,7 @@ impl Default for Controls { let controls = Self(HashMap::new()); // If you see this error, you may add new action in menu_actions - // or make sure that you have only one MenuAction with the same name in the MenuActions + // 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)); controls