118 lines
3.4 KiB
Rust
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));
|
|
}
|
|
}
|