Files
marathon/crates/lib/src/networking/change_detection.rs
2026-02-07 14:10:57 +00:00

118 lines
3.4 KiB
Rust

//! Change detection for networked entities
//!
//! This module provides systems that detect when networked components change
//! and prepare them for delta generation.
use bevy::prelude::*;
use crate::networking::{
NetworkedEntity,
NetworkedTransform,
};
/// System to automatically detect Transform changes and mark entity for sync
///
/// This system detects changes to Transform components on networked entities
/// and triggers persistence by accessing `NetworkedEntity` mutably (which marks
/// it as changed via Bevy's change detection).
///
/// Add this system to your app if you want automatic synchronization of
/// Transform changes:
///
/// ```no_run
/// use bevy::prelude::*;
/// use lib::networking::auto_detect_transform_changes_system;
///
/// App::new()
/// .add_systems(Update, auto_detect_transform_changes_system);
/// ```
pub fn auto_detect_transform_changes_system(
mut query: Query<
&mut NetworkedEntity,
(
With<NetworkedTransform>,
Or<(Changed<Transform>, Changed<GlobalTransform>)>,
),
>,
) {
// Simply accessing &mut NetworkedEntity triggers Bevy's change detection
for mut _networked in query.iter_mut() {
// No-op - the mutable access itself marks NetworkedEntity as changed
// This will trigger the delta generation system
}
}
/// Resource to track the last sync version for each entity
///
/// This helps us avoid sending redundant deltas for the same changes.
#[derive(Resource, Default)]
pub struct LastSyncVersions {
/// Map from network_id to the last vector clock we synced
versions: std::collections::HashMap<uuid::Uuid, u64>,
}
impl LastSyncVersions {
/// Check if we should sync this entity based on version
pub fn should_sync(&self, network_id: uuid::Uuid, version: u64) -> bool {
match self.versions.get(&network_id) {
Some(&last_version) => version > last_version,
None => true, // Never synced before
}
}
/// Update the last synced version for an entity
pub fn update(&mut self, network_id: uuid::Uuid, version: u64) {
self.versions.insert(network_id, version);
}
/// Remove tracking for an entity (when despawned)
pub fn remove(&mut self, network_id: uuid::Uuid) {
self.versions.remove(&network_id);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_last_sync_versions() {
let mut versions = LastSyncVersions::default();
let id = uuid::Uuid::new_v4();
// Should sync when never synced before
assert!(versions.should_sync(id, 1));
// Update to version 1
versions.update(id, 1);
// Should not sync same version
assert!(!versions.should_sync(id, 1));
// Should not sync older version
assert!(!versions.should_sync(id, 0));
// Should sync newer version
assert!(versions.should_sync(id, 2));
// Remove and should sync again
versions.remove(id);
assert!(versions.should_sync(id, 2));
}
#[test]
fn test_multiple_entities() {
let mut versions = LastSyncVersions::default();
let id1 = uuid::Uuid::new_v4();
let id2 = uuid::Uuid::new_v4();
versions.update(id1, 5);
versions.update(id2, 3);
assert!(!versions.should_sync(id1, 4));
assert!(versions.should_sync(id1, 6));
assert!(!versions.should_sync(id2, 2));
assert!(versions.should_sync(id2, 4));
}
}