From 0ad9f13f1d64dcf1ca1d544d67d8bd02ed6d66b2 Mon Sep 17 00:00:00 2001 From: DasLixou Date: Wed, 21 Aug 2024 13:01:03 +0200 Subject: [PATCH] I cracked reflection? --- crates/blenvy/src/components/fake_entity.rs | 2 +- crates/blenvy/src/components/mod.rs | 2 + crates/blenvy/src/components/patch_entity.rs | 35 ++++++++++++ .../src/components/reflect_ext/array_ext.rs | 36 ++++++++++++ .../src/components/reflect_ext/enum_ext.rs | 36 ++++++++++++ .../src/components/reflect_ext/list_ext.rs | 36 ++++++++++++ .../src/components/reflect_ext/map_ext.rs | 34 +++++++++++ .../blenvy/src/components/reflect_ext/mod.rs | 56 +++++++++++++++++++ .../src/components/reflect_ext/struct_ext.rs | 36 ++++++++++++ .../src/components/reflect_ext/tuple_ext.rs | 35 ++++++++++++ .../reflect_ext/tuple_struct_ext.rs | 35 ++++++++++++ .../ronstring_to_reflect_component.rs | 6 +- 12 files changed, 346 insertions(+), 3 deletions(-) create mode 100644 crates/blenvy/src/components/patch_entity.rs create mode 100644 crates/blenvy/src/components/reflect_ext/array_ext.rs create mode 100644 crates/blenvy/src/components/reflect_ext/enum_ext.rs create mode 100644 crates/blenvy/src/components/reflect_ext/list_ext.rs create mode 100644 crates/blenvy/src/components/reflect_ext/map_ext.rs create mode 100644 crates/blenvy/src/components/reflect_ext/mod.rs create mode 100644 crates/blenvy/src/components/reflect_ext/struct_ext.rs create mode 100644 crates/blenvy/src/components/reflect_ext/tuple_ext.rs create mode 100644 crates/blenvy/src/components/reflect_ext/tuple_struct_ext.rs diff --git a/crates/blenvy/src/components/fake_entity.rs b/crates/blenvy/src/components/fake_entity.rs index fa80971..e7d8c6a 100644 --- a/crates/blenvy/src/components/fake_entity.rs +++ b/crates/blenvy/src/components/fake_entity.rs @@ -1,5 +1,5 @@ pub(crate) struct Entity { - name: Option, + pub name: Option, } const _: () = { diff --git a/crates/blenvy/src/components/mod.rs b/crates/blenvy/src/components/mod.rs index 9c499f2..43a69e3 100644 --- a/crates/blenvy/src/components/mod.rs +++ b/crates/blenvy/src/components/mod.rs @@ -8,6 +8,8 @@ pub mod process_gltfs; pub use process_gltfs::*; mod fake_entity; +mod patch_entity; +mod reflect_ext; pub mod blender_settings; diff --git a/crates/blenvy/src/components/patch_entity.rs b/crates/blenvy/src/components/patch_entity.rs new file mode 100644 index 0000000..f9e763e --- /dev/null +++ b/crates/blenvy/src/components/patch_entity.rs @@ -0,0 +1,35 @@ +use bevy::{ + log::{info, warn}, + prelude::Entity, + reflect::Reflect, +}; + +use super::{fake_entity, reflect_ext}; + +pub fn patch_reflect_entity(reflect: &mut dyn Reflect) -> Option { + // We can put here either `fake_entity::Entity` or `bevy::ecs::entity::Entity`, but the latter would result in a false downcast. + let maybe_fake = reflect + .downcast_mut::() // TODO: doesn't work yet, seems like it doesnt work when it's a dynamic type + .map(|fake| fake.name.clone()); + + info!("{}", reflect.reflect_type_ident().unwrap()); + + if let Some(reference) = maybe_fake { + let entity = if let Some(name) = reference { + info!("Found name {name}"); + bevy::ecs::entity::Entity::PLACEHOLDER + } else { + warn!("No object was specified for Entity relation, using `Entity::PLACEHOLDER`."); + bevy::ecs::entity::Entity::PLACEHOLDER + }; + Some(entity) + } else { + let reflect_mut = reflect.reflect_mut(); + let iter = reflect_ext::DynamicFieldIterMut::from_reflect_mut(reflect_mut); + // TODO: recursively update + for f in iter { + patch_reflect_entity(f); + } + None + } +} diff --git a/crates/blenvy/src/components/reflect_ext/array_ext.rs b/crates/blenvy/src/components/reflect_ext/array_ext.rs new file mode 100644 index 0000000..e73a35b --- /dev/null +++ b/crates/blenvy/src/components/reflect_ext/array_ext.rs @@ -0,0 +1,36 @@ +use bevy::reflect::{Array, Reflect}; + +pub struct ArrayIterMut<'a> { + array: &'a mut dyn Array, + index: usize, +} + +impl<'a> ArrayIterMut<'a> { + /// Creates a new [`ArrayIterMut`]. + #[inline] + pub fn new(array: &'a mut dyn Array) -> ArrayIterMut { + ArrayIterMut { array, index: 0 } + } +} + +impl<'a> Iterator for ArrayIterMut<'a> { + type Item = &'a mut dyn Reflect; + + #[inline] + fn next(&mut self) -> Option { + let value = self.array.get_mut(self.index); + self.index += value.is_some() as usize; + value.map(|v| unsafe { + // SAFETY: index can only correspond to one field + &mut *(v as *mut _) + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let size = self.array.len(); + (size, Some(size)) + } +} + +impl<'a> ExactSizeIterator for ArrayIterMut<'a> {} diff --git a/crates/blenvy/src/components/reflect_ext/enum_ext.rs b/crates/blenvy/src/components/reflect_ext/enum_ext.rs new file mode 100644 index 0000000..5c15b73 --- /dev/null +++ b/crates/blenvy/src/components/reflect_ext/enum_ext.rs @@ -0,0 +1,36 @@ +use bevy::reflect::{Enum, Reflect}; + +pub struct VariantFieldIterMut<'a> { + container: &'a mut dyn Enum, + index: usize, +} + +impl<'a> VariantFieldIterMut<'a> { + pub fn new(container: &'a mut dyn Enum) -> Self { + Self { + container, + index: 0, + } + } +} + +impl<'a> Iterator for VariantFieldIterMut<'a> { + // TODO: make this work with `VariantFieldMut` again + type Item = &'a mut dyn Reflect; + + fn next(&mut self) -> Option { + let value = self.container.field_at_mut(self.index); + self.index += value.is_some() as usize; + value.map(|v| unsafe { + // SAFETY: index can only correspond to one field + &mut *(v as *mut _) + }) + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.container.field_len(); + (size, Some(size)) + } +} + +impl<'a> ExactSizeIterator for VariantFieldIterMut<'a> {} diff --git a/crates/blenvy/src/components/reflect_ext/list_ext.rs b/crates/blenvy/src/components/reflect_ext/list_ext.rs new file mode 100644 index 0000000..d8d17f6 --- /dev/null +++ b/crates/blenvy/src/components/reflect_ext/list_ext.rs @@ -0,0 +1,36 @@ +use bevy::reflect::{List, Reflect}; + +pub struct ListIterMut<'a> { + list: &'a mut dyn List, + index: usize, +} + +impl<'a> ListIterMut<'a> { + /// Creates a new [`ListIterMut`]. + #[inline] + pub fn new(list: &'a mut dyn List) -> ListIterMut { + ListIterMut { list, index: 0 } + } +} + +impl<'a> Iterator for ListIterMut<'a> { + type Item = &'a mut dyn Reflect; + + #[inline] + fn next(&mut self) -> Option { + let value = self.list.get_mut(self.index); + self.index += value.is_some() as usize; + value.map(|v| unsafe { + // SAFETY: index can only correspond to one field + &mut *(v as *mut _) + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let size = self.list.len(); + (size, Some(size)) + } +} + +impl<'a> ExactSizeIterator for ListIterMut<'a> {} diff --git a/crates/blenvy/src/components/reflect_ext/map_ext.rs b/crates/blenvy/src/components/reflect_ext/map_ext.rs new file mode 100644 index 0000000..d503a57 --- /dev/null +++ b/crates/blenvy/src/components/reflect_ext/map_ext.rs @@ -0,0 +1,34 @@ +use bevy::reflect::{Map, Reflect}; + +pub struct MapIterMut<'a> { + map: &'a mut dyn Map, + index: usize, +} + +impl<'a> MapIterMut<'a> { + /// Creates a new [`MapIterMut`]. + #[inline] + pub fn new(map: &'a mut dyn Map) -> MapIterMut { + MapIterMut { map, index: 0 } + } +} + +impl<'a> Iterator for MapIterMut<'a> { + type Item = (&'a dyn Reflect, &'a mut dyn Reflect); + + fn next(&mut self) -> Option { + let value = self.map.get_at_mut(self.index); + self.index += value.is_some() as usize; + value.map(|(k, v)| unsafe { + // SAFETY: index can only correspond to one field + (&*(k as *const _), &mut *(v as *mut _)) + }) + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.map.len(); + (size, Some(size)) + } +} + +impl<'a> ExactSizeIterator for MapIterMut<'a> {} diff --git a/crates/blenvy/src/components/reflect_ext/mod.rs b/crates/blenvy/src/components/reflect_ext/mod.rs new file mode 100644 index 0000000..15a63d5 --- /dev/null +++ b/crates/blenvy/src/components/reflect_ext/mod.rs @@ -0,0 +1,56 @@ +use bevy::reflect::{Reflect, ReflectMut}; + +pub mod array_ext; +pub mod enum_ext; +pub mod list_ext; +pub mod map_ext; +pub mod struct_ext; +pub mod tuple_ext; +pub mod tuple_struct_ext; + +pub enum DynamicFieldIterMut<'a> { + Struct(struct_ext::FieldIterMut<'a>), + TupleStruct(tuple_struct_ext::TupleStructFieldIterMut<'a>), + Tuple(tuple_ext::TupleFieldIterMut<'a>), + List(list_ext::ListIterMut<'a>), + Array(array_ext::ArrayIterMut<'a>), + Map(map_ext::MapIterMut<'a>), + Enum(enum_ext::VariantFieldIterMut<'a>), + Value, +} + +impl<'a> DynamicFieldIterMut<'a> { + pub fn from_reflect_mut(ref_mut: ReflectMut<'a>) -> Self { + match ref_mut { + ReflectMut::Struct(s) => DynamicFieldIterMut::Struct(struct_ext::FieldIterMut::new(s)), + ReflectMut::TupleStruct(s) => { + DynamicFieldIterMut::TupleStruct(tuple_struct_ext::TupleStructFieldIterMut::new(s)) + } + ReflectMut::Tuple(t) => { + DynamicFieldIterMut::Tuple(tuple_ext::TupleFieldIterMut::new(t)) + } + ReflectMut::List(l) => DynamicFieldIterMut::List(list_ext::ListIterMut::new(l)), + ReflectMut::Array(a) => DynamicFieldIterMut::Array(array_ext::ArrayIterMut::new(a)), + ReflectMut::Map(m) => DynamicFieldIterMut::Map(map_ext::MapIterMut::new(m)), + ReflectMut::Enum(e) => DynamicFieldIterMut::Enum(enum_ext::VariantFieldIterMut::new(e)), + ReflectMut::Value(_) => DynamicFieldIterMut::Value, + } + } +} + +impl<'a> Iterator for DynamicFieldIterMut<'a> { + type Item = &'a mut dyn Reflect; + + fn next(&mut self) -> Option { + match self { + DynamicFieldIterMut::Struct(s) => s.next(), + DynamicFieldIterMut::TupleStruct(s) => s.next(), + DynamicFieldIterMut::Tuple(t) => t.next(), + DynamicFieldIterMut::List(l) => l.next(), + DynamicFieldIterMut::Array(a) => a.next(), + DynamicFieldIterMut::Map(m) => m.next().map(|(_, v)| v), + DynamicFieldIterMut::Enum(e) => e.next(), + DynamicFieldIterMut::Value => None, + } + } +} diff --git a/crates/blenvy/src/components/reflect_ext/struct_ext.rs b/crates/blenvy/src/components/reflect_ext/struct_ext.rs new file mode 100644 index 0000000..d79e62d --- /dev/null +++ b/crates/blenvy/src/components/reflect_ext/struct_ext.rs @@ -0,0 +1,36 @@ +use bevy::reflect::{Reflect, Struct}; + +/// An iterator over the field values of a struct. +pub struct FieldIterMut<'a> { + pub(crate) struct_val: &'a mut dyn Struct, + pub(crate) index: usize, +} + +impl<'a> FieldIterMut<'a> { + pub fn new(value: &'a mut dyn Struct) -> Self { + FieldIterMut { + struct_val: value, + index: 0, + } + } +} + +impl<'a> Iterator for FieldIterMut<'a> { + type Item = &'a mut dyn Reflect; + + fn next(&mut self) -> Option { + let value = self.struct_val.field_at_mut(self.index); + self.index += value.is_some() as usize; + value.map(|v| unsafe { + // SAFETY: index can only correspond to one field + &mut *(v as *mut _) + }) + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.struct_val.field_len(); + (size, Some(size)) + } +} + +impl<'a> ExactSizeIterator for FieldIterMut<'a> {} diff --git a/crates/blenvy/src/components/reflect_ext/tuple_ext.rs b/crates/blenvy/src/components/reflect_ext/tuple_ext.rs new file mode 100644 index 0000000..cb67980 --- /dev/null +++ b/crates/blenvy/src/components/reflect_ext/tuple_ext.rs @@ -0,0 +1,35 @@ +use bevy::reflect::{Reflect, Tuple}; + +pub struct TupleFieldIterMut<'a> { + pub(crate) tuple: &'a mut dyn Tuple, + pub(crate) index: usize, +} + +impl<'a> TupleFieldIterMut<'a> { + pub fn new(value: &'a mut dyn Tuple) -> Self { + TupleFieldIterMut { + tuple: value, + index: 0, + } + } +} + +impl<'a> Iterator for TupleFieldIterMut<'a> { + type Item = &'a mut dyn Reflect; + + fn next(&mut self) -> Option { + let value = self.tuple.field_mut(self.index); + self.index += value.is_some() as usize; + value.map(|v| unsafe { + // SAFETY: index can only correspond to one field + &mut *(v as *mut _) + }) + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.tuple.field_len(); + (size, Some(size)) + } +} + +impl<'a> ExactSizeIterator for TupleFieldIterMut<'a> {} diff --git a/crates/blenvy/src/components/reflect_ext/tuple_struct_ext.rs b/crates/blenvy/src/components/reflect_ext/tuple_struct_ext.rs new file mode 100644 index 0000000..c189ea8 --- /dev/null +++ b/crates/blenvy/src/components/reflect_ext/tuple_struct_ext.rs @@ -0,0 +1,35 @@ +use bevy::reflect::{Reflect, TupleStruct}; + +pub struct TupleStructFieldIterMut<'a> { + pub(crate) tuple_struct: &'a mut dyn TupleStruct, + pub(crate) index: usize, +} + +impl<'a> TupleStructFieldIterMut<'a> { + pub fn new(value: &'a mut dyn TupleStruct) -> Self { + TupleStructFieldIterMut { + tuple_struct: value, + index: 0, + } + } +} + +impl<'a> Iterator for TupleStructFieldIterMut<'a> { + type Item = &'a mut dyn Reflect; + + fn next(&mut self) -> Option { + let value = self.tuple_struct.field_mut(self.index); + self.index += value.is_some() as usize; + value.map(|v| unsafe { + // SAFETY: index can only correspond to one field + &mut *(v as *mut _) + }) + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.tuple_struct.field_len(); + (size, Some(size)) + } +} + +impl<'a> ExactSizeIterator for TupleStructFieldIterMut<'a> {} diff --git a/crates/blenvy/src/components/ronstring_to_reflect_component.rs b/crates/blenvy/src/components/ronstring_to_reflect_component.rs index ba1a473..aa83af7 100644 --- a/crates/blenvy/src/components/ronstring_to_reflect_component.rs +++ b/crates/blenvy/src/components/ronstring_to_reflect_component.rs @@ -7,7 +7,7 @@ use bevy::utils::HashMap; use ron::Value; use serde::de::DeserializeSeed; -use super::{capitalize_first_letter, fake_entity}; +use super::{capitalize_first_letter, fake_entity, patch_entity::patch_reflect_entity}; pub fn ronstring_to_reflect_component( ron_string: &str, @@ -127,7 +127,7 @@ fn bevy_components_string_to_components( let mut deserializer = ron::Deserializer::from_str(ron_string.as_str()) .expect("deserialzer should have been generated from string"); let reflect_deserializer = ReflectDeserializer::new(type_registry); - let component = reflect_deserializer + let mut component = reflect_deserializer .deserialize(&mut deserializer) .unwrap_or_else(|e| { panic!( @@ -136,6 +136,8 @@ fn bevy_components_string_to_components( ) }); + patch_reflect_entity(component.as_mut()); + debug!("component {:?}", component); debug!("real type {:?}", component.get_represented_type_info()); components.push((component, type_registration.clone()));