chore: honestly fixed so much and forgot to commit

Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
This commit is contained in:
2025-12-28 17:39:27 +00:00
parent f9f289f5b2
commit d1d3aec8aa
47 changed files with 2248 additions and 438 deletions

View File

@@ -34,6 +34,7 @@ use crate::networking::{
LastSyncVersions,
auto_detect_transform_changes_system,
},
components::{NetworkedEntity, NetworkedTransform},
delta_generation::{
NodeVectorClock,
generate_delta_system,
@@ -43,8 +44,10 @@ use crate::networking::{
cleanup_despawned_entities_system,
register_networked_entities_system,
},
gossip_bridge::GossipBridge,
locks::{
EntityLockRegistry,
acquire_locks_on_selection_system,
broadcast_lock_heartbeats_system,
cleanup_expired_locks_system,
release_locks_on_deselection_system,
@@ -59,6 +62,7 @@ use crate::networking::{
initialize_session_system,
save_session_on_shutdown_system,
},
sync_component::Synced,
tombstones::{
TombstoneRegistry,
garbage_collect_tombstones_system,
@@ -142,6 +146,104 @@ impl SessionSecret {
}
}
/// System that auto-inserts required sync components when `Synced` marker is detected.
///
/// This system runs in PreUpdate and automatically adds:
/// - `NetworkedEntity` with a new UUID and node ID
/// - `Persisted` with the same UUID
/// - `NetworkedTransform` if the entity has a `Transform` component
///
/// Note: Selection is now a global `LocalSelection` resource, not a per-entity component.
///
/// This eliminates the need for users to manually add these components when spawning synced entities.
fn auto_insert_sync_components(
mut commands: Commands,
query: Query<Entity, (Added<Synced>, Without<NetworkedEntity>)>,
node_clock: Res<NodeVectorClock>,
// We need access to check if entity has Transform
transforms: Query<&Transform>,
) {
for entity in &query {
let entity_id = uuid::Uuid::new_v4();
let node_id = node_clock.node_id;
// Always add NetworkedEntity and Persisted
let mut entity_commands = commands.entity(entity);
entity_commands.insert((
NetworkedEntity::with_id(entity_id, node_id),
crate::persistence::Persisted::with_id(entity_id),
));
// Auto-add NetworkedTransform if entity has Transform
if transforms.contains(entity) {
entity_commands.insert(NetworkedTransform);
}
debug!("Auto-inserted sync components for entity {:?} (UUID: {})", entity, entity_id);
}
}
/// System that adds NetworkedTransform to networked entities when Transform is added.
///
/// This handles entities received from the network that already have NetworkedEntity,
/// Persisted, and Synced, but need NetworkedTransform when Transform is added.
fn auto_insert_networked_transform(
mut commands: Commands,
query: Query<
Entity,
(
With<NetworkedEntity>,
With<Synced>,
Added<Transform>,
Without<NetworkedTransform>,
),
>,
) {
for entity in &query {
commands.entity(entity).insert(NetworkedTransform);
debug!("Auto-inserted NetworkedTransform for networked entity {:?}", entity);
}
}
/// System that triggers anti-entropy sync when going online (GossipBridge added).
///
/// This handles the offline-to-online transition: when GossipBridge is inserted,
/// we immediately send a SyncRequest to trigger anti-entropy and broadcast all
/// operations from the operation log.
///
/// Uses a Local resource to track if we've already sent the sync request, so this only runs once.
fn trigger_sync_on_connect(
mut has_synced: Local<bool>,
bridge: Res<GossipBridge>,
node_clock: Res<NodeVectorClock>,
operation_log: Res<OperationLog>,
) {
if *has_synced {
return; // Already did this
}
let op_count = operation_log.total_operations();
debug!(
"Going online: triggering anti-entropy sync to broadcast {} offline operations",
op_count
);
// Send a SyncRequest to trigger anti-entropy
// This will cause the message_dispatcher to respond with all operations from our log
let request = crate::networking::operation_log::build_sync_request(
node_clock.node_id,
node_clock.clock.clone(),
);
if let Err(e) = bridge.send(request) {
error!("Failed to send SyncRequest on connect: {}", e);
} else {
debug!("Sent SyncRequest to trigger anti-entropy sync");
}
*has_synced = true;
}
/// Bevy plugin for CRDT networking
///
/// This plugin sets up all systems and resources needed for distributed
@@ -236,7 +338,8 @@ impl Plugin for NetworkingPlugin {
.insert_resource(OperationLog::new())
.insert_resource(TombstoneRegistry::new())
.insert_resource(EntityLockRegistry::new())
.insert_resource(crate::networking::ComponentVectorClocks::new());
.insert_resource(crate::networking::ComponentVectorClocks::new())
.insert_resource(crate::networking::LocalSelection::new());
// Startup systems - initialize session from persistence
app.add_systems(Startup, initialize_session_system);
@@ -245,12 +348,16 @@ impl Plugin for NetworkingPlugin {
app.add_systems(
PreUpdate,
(
// Auto-insert sync components when Synced marker is added (must run first)
auto_insert_sync_components,
// Register new networked entities
register_networked_entities_system,
// Central message dispatcher - handles all incoming messages
// This replaces the individual message handling systems and
// eliminates O(n²) behavior from multiple systems polling the same queue
message_dispatcher_system,
// Auto-insert NetworkedTransform for networked entities when Transform is added
auto_insert_networked_transform,
)
.chain(),
);
@@ -263,11 +370,20 @@ impl Plugin for NetworkingPlugin {
auto_detect_transform_changes_system,
// Handle local entity deletions
handle_local_deletions_system,
// Acquire locks when entities are selected
acquire_locks_on_selection_system,
// Release locks when entities are deselected
release_locks_on_deselection_system,
),
);
// Trigger anti-entropy sync when going online (separate from chain to allow conditional execution)
app.add_systems(
PostUpdate,
trigger_sync_on_connect
.run_if(bevy::ecs::schedule::common_conditions::resource_exists::<GossipBridge>),
);
// PostUpdate systems - generate and send deltas
app.add_systems(
PostUpdate,