use std::any::Any;
use std::fmt::Debug;
use std::sync::{LazyLock, RwLock};
use bevy::app::App;
use bevy::prelude::{FromReflect, Reflect, ReflectDeserialize, ReflectSerialize, TypePath, Vec2};
use bevy::reflect::utility::{GenericTypePathCell, NonGenericTypeInfoCell};
use bevy::reflect::{
FromType, GetTypeRegistration, OpaqueInfo, PartialReflect, ReflectFromPtr, ReflectKind,
ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeRegistration, Typed, erased_serde,
};
use dyn_clone::DynClone;
use dyn_eq::DynEq;
use dyn_hash::DynHash;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_flexitos::ser::require_erased_serialize_impl;
use serde_flexitos::{Registry, serialize_trait_object};
use crate::input_processing::DualAxisProcessor;
use crate::typetag::{InfallibleMapRegistry, RegisterTypeTag};
pub trait CustomDualAxisProcessor:
Send + Sync + Debug + DynClone + DynEq + DynHash + Reflect + erased_serde::Serialize
{
fn process(&self, input_value: Vec2) -> Vec2;
}
impl<P: CustomDualAxisProcessor> From<P> for DualAxisProcessor {
fn from(value: P) -> Self {
Self::Custom(Box::new(value))
}
}
dyn_clone::clone_trait_object!(CustomDualAxisProcessor);
dyn_eq::eq_trait_object!(CustomDualAxisProcessor);
dyn_hash::hash_trait_object!(CustomDualAxisProcessor);
impl PartialReflect for Box<dyn CustomDualAxisProcessor> {
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(Self::type_info())
}
fn reflect_kind(&self) -> ReflectKind {
ReflectKind::Opaque
}
fn reflect_ref(&self) -> ReflectRef<'_> {
ReflectRef::Opaque(self)
}
fn reflect_mut(&mut self) -> ReflectMut<'_> {
ReflectMut::Opaque(self)
}
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Opaque(self)
}
fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), bevy::reflect::ApplyError> {
if let Some(value) = value.try_downcast_ref::<Self>() {
*self = value.clone();
Ok(())
} else {
Err(bevy::reflect::ApplyError::MismatchedTypes {
from_type: self
.reflect_type_ident()
.unwrap_or_default()
.to_string()
.into_boxed_str(),
to_type: self
.reflect_type_ident()
.unwrap_or_default()
.to_string()
.into_boxed_str(),
})
}
}
fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {
self
}
fn as_partial_reflect(&self) -> &dyn PartialReflect {
self
}
fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {
self
}
fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {
Ok(self)
}
fn try_as_reflect(&self) -> Option<&dyn Reflect> {
Some(self)
}
fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {
Some(self)
}
}
impl Reflect for Box<dyn CustomDualAxisProcessor> {
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
fn as_reflect(&self) -> &dyn Reflect {
self
}
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
}
impl Typed for Box<dyn CustomDualAxisProcessor> {
fn type_info() -> &'static TypeInfo {
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
CELL.get_or_set(|| TypeInfo::Opaque(OpaqueInfo::new::<Self>()))
}
}
impl TypePath for Box<dyn CustomDualAxisProcessor> {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
{
format!(
"std::boxed::Box<dyn {}::CustomDualAxisProcessor>",
module_path!()
)
}
})
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| "Box<dyn CustomDualAxisProcessor>".to_string())
}
fn type_ident() -> Option<&'static str> {
Some("Box<dyn CustomDualAxisProcessor>")
}
fn crate_name() -> Option<&'static str> {
Some(module_path!().split(':').next().unwrap())
}
fn module_path() -> Option<&'static str> {
Some(module_path!())
}
}
impl GetTypeRegistration for Box<dyn CustomDualAxisProcessor> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
registration.insert::<ReflectDeserialize>(FromType::<Self>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
registration.insert::<ReflectSerialize>(FromType::<Self>::from_type());
registration
}
}
impl FromReflect for Box<dyn CustomDualAxisProcessor> {
fn from_reflect(reflect: &dyn PartialReflect) -> Option<Self> {
Some(reflect.try_downcast_ref::<Self>()?.clone())
}
}
impl Serialize for dyn CustomDualAxisProcessor + '_ {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
const fn __check_erased_serialize_super_trait<T: ?Sized + CustomDualAxisProcessor>() {
require_erased_serialize_impl::<T>();
}
serialize_trait_object(serializer, self.reflect_short_type_path(), self)
}
}
impl<'de> Deserialize<'de> for Box<dyn CustomDualAxisProcessor> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let registry = PROCESSOR_REGISTRY.read().unwrap();
registry.deserialize_trait_object(deserializer)
}
}
static PROCESSOR_REGISTRY: LazyLock<RwLock<InfallibleMapRegistry<dyn CustomDualAxisProcessor>>> =
LazyLock::new(|| RwLock::new(InfallibleMapRegistry::new("CustomDualAxisProcessor")));
pub trait RegisterDualAxisProcessorExt {
fn register_dual_axis_processor<'de, T>(&mut self) -> &mut Self
where
T: RegisterTypeTag<'de, dyn CustomDualAxisProcessor> + GetTypeRegistration;
}
impl RegisterDualAxisProcessorExt for App {
fn register_dual_axis_processor<'de, T>(&mut self) -> &mut Self
where
T: RegisterTypeTag<'de, dyn CustomDualAxisProcessor> + GetTypeRegistration,
{
let mut registry = PROCESSOR_REGISTRY.write().unwrap();
T::register_typetag(&mut registry);
self.register_type::<T>();
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate as leafwing_input_manager;
use leafwing_input_manager_macros::serde_typetag;
use serde_test::{Token, assert_tokens};
#[test]
fn test_custom_dual_axis_processor() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
struct CustomDualAxisInverted;
#[serde_typetag]
impl CustomDualAxisProcessor for CustomDualAxisInverted {
fn process(&self, input_value: Vec2) -> Vec2 {
-input_value
}
}
let mut app = App::new();
app.register_dual_axis_processor::<CustomDualAxisInverted>();
let custom: Box<dyn CustomDualAxisProcessor> = Box::new(CustomDualAxisInverted);
assert_tokens(
&custom,
&[
Token::Map { len: Some(1) },
Token::BorrowedStr("CustomDualAxisInverted"),
Token::UnitStruct {
name: "CustomDualAxisInverted",
},
Token::MapEnd,
],
);
let processor = DualAxisProcessor::Custom(custom);
assert_eq!(DualAxisProcessor::from(CustomDualAxisInverted), processor);
for x in -300..300 {
let x = x as f32 * 0.01;
for y in -300..300 {
let y = y as f32 * 0.01;
let value = Vec2::new(x, y);
assert_eq!(processor.process(value), -value);
assert_eq!(CustomDualAxisInverted.process(value), -value);
}
}
}
}