//! Reflection-based component serialization for persistence //! //! This module provides utilities to serialize and deserialize Bevy components //! using reflection, allowing the persistence layer to work with any component //! that implements Reflect. use bevy::{ prelude::*, reflect::{ TypeRegistry, serde::{ ReflectSerializer, TypedReflectDeserializer, TypedReflectSerializer, }, }, }; use bincode::Options as _; use serde::de::DeserializeSeed; use crate::persistence::error::{ PersistenceError, Result, }; /// Marker component to indicate that an entity should be persisted /// /// Add this component to any entity that should have its state persisted to /// disk. The persistence system will automatically serialize all components on /// entities with this marker when they change. /// /// # Triggering Persistence /// /// To trigger persistence after modifying components on an entity, access /// `Persisted` mutably through a query. Bevy's change detection will /// automatically mark it as changed: /// /// ```no_run /// # use bevy::prelude::*; /// # use lib::persistence::*; /// fn update_position(mut query: Query<(&mut Transform, &mut Persisted)>) { /// for (mut transform, mut persisted) in query.iter_mut() { /// transform.translation.x += 1.0; /// // Accessing &mut Persisted triggers change detection automatically /// } /// } /// ``` /// /// Alternatively, use `auto_track_transform_changes_system` for automatic /// persistence of Transform changes without manual queries. #[derive(Component, Reflect, Default)] #[reflect(Component)] pub struct Persisted { /// Unique network ID for this entity pub network_id: uuid::Uuid, } impl Persisted { pub fn new() -> Self { Self { network_id: uuid::Uuid::new_v4(), } } pub fn with_id(network_id: uuid::Uuid) -> Self { Self { network_id } } } /// Trait for components that can be persisted pub trait Persistable: Component + Reflect { /// Get the type name for this component (used as key in database) fn type_name() -> &'static str { std::any::type_name::() } } /// Serialize a component using Bevy's reflection system /// /// This converts any component implementing `Reflect` into bytes for storage. /// Uses bincode for efficient binary serialization with type information from /// the registry to handle polymorphic types correctly. /// /// # Parameters /// - `component`: Component to serialize (must implement `Reflect`) /// - `type_registry`: Bevy's type registry for reflection metadata /// /// # Returns /// - `Ok(Vec)`: Serialized component data /// - `Err`: If serialization fails (e.g., type not properly registered) /// /// # Examples /// ```no_run /// # use bevy::prelude::*; /// # use lib::persistence::*; /// # fn example(component: &Transform, registry: &AppTypeRegistry) -> anyhow::Result<()> { /// let registry = registry.read(); /// let bytes = serialize_component(component.as_reflect(), ®istry)?; /// # Ok(()) /// # } /// ``` pub fn serialize_component( component: &dyn Reflect, type_registry: &TypeRegistry, ) -> Result> { let serializer = ReflectSerializer::new(component, type_registry); bincode::options() .serialize(&serializer) .map_err(PersistenceError::from) } /// Serialize a component when the type is known (more efficient for bincode) /// /// This uses `TypedReflectSerializer` which doesn't include type path /// information, making it compatible with `TypedReflectDeserializer` for binary /// formats. pub fn serialize_component_typed( component: &dyn Reflect, type_registry: &TypeRegistry, ) -> Result> { let serializer = TypedReflectSerializer::new(component, type_registry); bincode::options() .serialize(&serializer) .map_err(PersistenceError::from) } /// Deserialize a component using Bevy's reflection system /// /// Converts serialized bytes back into a reflected component. The returned /// component is boxed and must be downcast to the concrete type for use. /// /// # Parameters /// - `bytes`: Serialized component data from [`serialize_component`] /// - `type_registry`: Bevy's type registry for reflection metadata /// /// # Returns /// - `Ok(Box)`: Deserialized component (needs downcasting) /// - `Err`: If deserialization fails (e.g., type not registered, data /// corruption) /// /// # Examples /// ```no_run /// # use bevy::prelude::*; /// # use lib::persistence::*; /// # fn example(bytes: &[u8], registry: &AppTypeRegistry) -> anyhow::Result<()> { /// let registry = registry.read(); /// let reflected = deserialize_component(bytes, ®istry)?; /// // Downcast to concrete type as needed /// # Ok(()) /// # } /// ``` pub fn deserialize_component( bytes: &[u8], type_registry: &TypeRegistry, ) -> Result> { let mut deserializer = bincode::Deserializer::from_slice(bytes, bincode::options()); let reflect_deserializer = bevy::reflect::serde::ReflectDeserializer::new(type_registry); reflect_deserializer .deserialize(&mut deserializer) .map_err(|e| PersistenceError::Deserialization(e.to_string())) } /// Deserialize a component when the type is known /// /// Uses `TypedReflectDeserializer` which is more efficient for binary formats /// like bincode when the component type is known at deserialization time. pub fn deserialize_component_typed( bytes: &[u8], component_type: &str, type_registry: &TypeRegistry, ) -> Result> { let registration = type_registry .get_with_type_path(component_type) .ok_or_else(|| { PersistenceError::Deserialization(format!("Type {} not registered", component_type)) })?; let mut deserializer = bincode::Deserializer::from_slice(bytes, bincode::options()); let reflect_deserializer = TypedReflectDeserializer::new(registration, type_registry); reflect_deserializer .deserialize(&mut deserializer) .map_err(|e| PersistenceError::Deserialization(e.to_string())) } /// Serialize a component directly from an entity using its type path /// /// This is a convenience function that combines type lookup, reflection, and /// serialization. It's the primary method used by the persistence system to /// save component state without knowing the concrete type at compile time. /// /// # Parameters /// - `entity`: Bevy entity to read the component from /// - `component_type`: Type path string (e.g., /// "bevy_transform::components::Transform") /// - `world`: Bevy world containing the entity /// - `type_registry`: Bevy's type registry for reflection metadata /// /// # Returns /// - `Some(Vec)`: Serialized component data /// - `None`: If entity doesn't have the component or type isn't registered /// /// # Examples /// ```no_run /// # use bevy::prelude::*; /// # use lib::persistence::*; /// # fn example(entity: Entity, world: &World, registry: &AppTypeRegistry) -> Option<()> { /// let registry = registry.read(); /// let bytes = serialize_component_from_entity( /// entity, /// "bevy_transform::components::Transform", /// world, /// ®istry, /// )?; /// # Some(()) /// # } /// ``` pub fn serialize_component_from_entity( entity: Entity, component_type: &str, world: &World, type_registry: &TypeRegistry, ) -> Option> { // Get the type registration let registration = type_registry.get_with_type_path(component_type)?; // Get the ReflectComponent data let reflect_component = registration.data::()?; // Reflect the component from the entity let reflected = reflect_component.reflect(world.entity(entity))?; // Serialize it directly serialize_component(reflected, type_registry).ok() } /// Serialize all components from an entity that have reflection data /// /// This iterates over all components on an entity and serializes those that: /// - Are registered in the type registry /// - Have `ReflectComponent` data (meaning they support reflection) /// - Are not the `Persisted` marker component (to avoid redundant storage) /// /// # Parameters /// - `entity`: Bevy entity to serialize components from /// - `world`: Bevy world containing the entity /// - `type_registry`: Bevy's type registry for reflection metadata /// /// # Returns /// Vector of tuples containing (component_type_path, serialized_data) for each /// component pub fn serialize_all_components_from_entity( entity: Entity, world: &World, type_registry: &TypeRegistry, ) -> Vec<(String, Vec)> { let mut components = Vec::new(); // Get the entity reference let entity_ref = world.entity(entity); // Iterate over all type registrations for registration in type_registry.iter() { // Skip if no ReflectComponent data (not a component) let Some(reflect_component) = registration.data::() else { continue; }; // Get the type path for this component let type_path = registration.type_info().type_path(); // Skip the Persisted marker component itself (we don't need to persist it) if type_path.ends_with("::Persisted") { continue; } // Try to reflect this component from the entity if let Some(reflected) = reflect_component.reflect(entity_ref) { // Serialize the component using typed serialization for consistency // This matches the format expected by deserialize_component_typed if let Ok(data) = serialize_component_typed(reflected, type_registry) { components.push((type_path.to_string(), data)); } } } components } #[cfg(test)] mod tests { use super::*; #[derive(Component, Reflect, Default)] #[reflect(Component)] struct TestComponent { value: i32, } #[test] fn test_component_serialization() -> Result<()> { let mut registry = TypeRegistry::default(); registry.register::(); let component = TestComponent { value: 42 }; let bytes = serialize_component(&component, ®istry)?; assert!(!bytes.is_empty()); Ok(()) } }