checkpoint

Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
This commit is contained in:
2025-12-04 19:49:48 +00:00
parent a76306342d
commit 93ab598db0
10 changed files with 34049 additions and 506 deletions

View File

@@ -46,6 +46,7 @@ mod operations;
mod orset;
mod plugin;
mod rga;
mod sync_component;
mod tombstones;
mod vector_clock;
@@ -67,5 +68,6 @@ pub use operations::*;
pub use orset::*;
pub use plugin::*;
pub use rga::*;
pub use sync_component::*;
pub use tombstones::*;
pub use vector_clock::*;

View File

@@ -0,0 +1,160 @@
//! Sync Component trait and supporting types for RFC 0003
//!
//! This module defines the core trait that all synced components implement,
//! along with the types used for strategy selection and merge decisions.
use bevy::prelude::*;
/// Sync strategy enum - determines how conflicts are resolved
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SyncStrategy {
/// Last-Write-Wins: Newer timestamp wins, node ID tiebreaker for concurrent
LastWriteWins,
/// OR-Set: Observed-Remove Set for collections
Set,
/// Sequence: RGA (Replicated Growable Array) for ordered lists
Sequence,
/// Custom: User-defined conflict resolution
Custom,
}
/// Result of comparing vector clocks
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClockComparison {
/// Remote vector clock is strictly newer
RemoteNewer,
/// Local vector clock is strictly newer
LocalNewer,
/// Concurrent (neither is newer)
Concurrent,
}
/// Decision made during component merge operation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComponentMergeDecision {
/// Kept local value
KeptLocal,
/// Took remote value
TookRemote,
/// Merged both (for CRDTs)
Merged,
}
/// Core trait for synced components
///
/// This trait is automatically implemented by the `#[derive(Synced)]` macro.
/// All synced components must implement this trait.
///
/// # Example
/// ```
/// use bevy::prelude::*;
/// use lib::networking::{SyncComponent, SyncStrategy, ClockComparison, ComponentMergeDecision};
///
/// // Example showing what the trait looks like - normally generated by #[derive(Synced)]
/// #[derive(Component, Reflect, Clone, serde::Serialize, serde::Deserialize)]
/// struct Health(f32);
///
/// // The SyncComponent trait defines these methods that the macro generates
/// // You can serialize and deserialize components for sync
/// ```
pub trait SyncComponent: Component + Reflect + Sized {
/// Schema version for this component
const VERSION: u32;
/// Sync strategy for conflict resolution
const STRATEGY: SyncStrategy;
/// Serialize this component to bytes
///
/// Uses bincode for efficient binary serialization.
fn serialize_sync(&self) -> anyhow::Result<Vec<u8>>;
/// Deserialize this component from bytes
///
/// Uses bincode to deserialize from the format created by `serialize_sync`.
fn deserialize_sync(data: &[u8]) -> anyhow::Result<Self>;
/// Merge remote state with local state
///
/// The merge logic is strategy-specific:
/// - **LWW**: Takes newer value based on vector clock, uses tiebreaker for concurrent
/// - **Set**: Merges both sets (OR-Set semantics)
/// - **Sequence**: Merges sequences preserving order (RGA semantics)
/// - **Custom**: Calls user-defined ConflictResolver
///
/// # Arguments
/// * `remote` - The remote state to merge
/// * `clock_cmp` - Result of comparing local and remote vector clocks
///
/// # Returns
/// Decision about what happened during the merge
fn merge(&mut self, remote: Self, clock_cmp: ClockComparison) -> ComponentMergeDecision;
}
/// Marker component for entities that should be synced
///
/// Add this to any entity with synced components to enable automatic
/// change detection and synchronization.
///
/// # Example
/// ```
/// use bevy::prelude::*;
/// use lib::networking::Synced;
/// use sync_macros::Synced as SyncedDerive;
///
/// #[derive(Component, Reflect, Clone, serde::Serialize, serde::Deserialize)]
/// #[derive(SyncedDerive)]
/// #[sync(version = 1, strategy = "LastWriteWins")]
/// struct Health(f32);
///
/// #[derive(Component, Reflect, Clone, serde::Serialize, serde::Deserialize)]
/// #[derive(SyncedDerive)]
/// #[sync(version = 1, strategy = "LastWriteWins")]
/// struct Position { x: f32, y: f32 }
///
/// let mut world = World::new();
/// world.spawn((
/// Health(100.0),
/// Position { x: 0.0, y: 0.0 },
/// Synced, // Marker enables sync
/// ));
/// ```
#[derive(Component, Reflect, Default, Clone, Copy)]
#[reflect(Component)]
pub struct Synced;
/// Diagnostic component for debugging sync issues
///
/// Add this to an entity to get detailed diagnostic output about
/// its sync status.
///
/// # Example
/// ```
/// use bevy::prelude::*;
/// use lib::networking::DiagnoseSync;
///
/// let mut world = World::new();
/// let entity = world.spawn_empty().id();
/// world.entity_mut(entity).insert(DiagnoseSync);
/// // A diagnostic system will check this entity and log sync status
/// ```
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
pub struct DiagnoseSync;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strategy_enum_works() {
assert_eq!(SyncStrategy::LastWriteWins, SyncStrategy::LastWriteWins);
assert_ne!(SyncStrategy::LastWriteWins, SyncStrategy::Set);
}
#[test]
fn clock_comparison_works() {
assert_eq!(ClockComparison::RemoteNewer, ClockComparison::RemoteNewer);
assert_ne!(ClockComparison::RemoteNewer, ClockComparison::LocalNewer);
}
}