diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index c7b2dec..df844da 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -289,6 +289,9 @@ fn main() { app.add_plugins(CubePlugin); app.add_systems(Startup, initialize_offline_resources); + // Configure fixed timestep for deterministic game logic at 60fps + app.insert_resource(Time::::from_hz(60.0)); + // Insert control socket path as resource app.insert_resource(control::ControlSocketPath(args.control_socket.clone())); app.add_systems(Startup, control::start_control_socket_system); diff --git a/crates/libmarathon/src/engine/networking.rs b/crates/libmarathon/src/engine/networking.rs index 6e358ae..6b0c89c 100644 --- a/crates/libmarathon/src/engine/networking.rs +++ b/crates/libmarathon/src/engine/networking.rs @@ -185,10 +185,12 @@ impl NetworkingManager { // Spawn background task to maintain DHT presence let session_id_clone = session_id.clone(); + let cancel_token_clone = cancel_token.clone(); tokio::spawn(crate::engine::peer_discovery::maintain_dht_presence( session_id_clone, endpoint_id, pkarr_client, + cancel_token_clone, )); let manager = Self { diff --git a/crates/libmarathon/src/engine/peer_discovery.rs b/crates/libmarathon/src/engine/peer_discovery.rs index 922d89a..2d10356 100644 --- a/crates/libmarathon/src/engine/peer_discovery.rs +++ b/crates/libmarathon/src/engine/peer_discovery.rs @@ -138,14 +138,21 @@ pub async fn maintain_dht_presence( session_id: SessionId, our_endpoint_id: EndpointId, dht_client: pkarr::Client, + cancel_token: tokio_util::sync::CancellationToken, ) { let mut interval = tokio::time::interval(Duration::from_secs(30 * 60)); // 30 minutes loop { - interval.tick().await; - - if let Err(e) = publish_peer_to_dht(&session_id, our_endpoint_id, &dht_client).await { - tracing::warn!("Failed to republish to DHT: {}", e); + tokio::select! { + _ = cancel_token.cancelled() => { + tracing::info!("DHT maintenance task shutting down"); + break; + } + _ = interval.tick() => { + if let Err(e) = publish_peer_to_dht(&session_id, our_endpoint_id, &dht_client).await { + tracing::warn!("Failed to republish to DHT: {}", e); + } + } } } } diff --git a/crates/libmarathon/src/networking/plugin.rs b/crates/libmarathon/src/networking/plugin.rs index 45e9616..7fc36fc 100644 --- a/crates/libmarathon/src/networking/plugin.rs +++ b/crates/libmarathon/src/networking/plugin.rs @@ -417,9 +417,9 @@ impl Plugin for NetworkingPlugin { .chain(), ); - // Update systems - handle local operations + // FixedUpdate systems - game logic at locked 60fps app.add_systems( - Update, + FixedUpdate, ( // Track Transform changes and mark NetworkedTransform as changed auto_detect_transform_changes_system, @@ -438,14 +438,14 @@ impl Plugin for NetworkingPlugin { // Trigger anti-entropy sync when going online (separate from chain to allow conditional execution) app.add_systems( - PostUpdate, + FixedPostUpdate, trigger_sync_on_connect .run_if(bevy::ecs::schedule::common_conditions::resource_exists::), ); - // PostUpdate systems - generate and send deltas + // FixedPostUpdate systems - generate and send deltas at locked 60fps app.add_systems( - PostUpdate, + FixedPostUpdate, ( // Generate deltas for changed entities, then cleanup markers // CRITICAL: cleanup_skip_delta_markers_system must run immediately after