fix keyboard input and app shutdown freeze

Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
This commit is contained in:
2025-12-24 18:18:27 +00:00
parent 3e840908f6
commit 28909e8b76
7 changed files with 296 additions and 72 deletions

View File

@@ -1,6 +1,7 @@
//! Core Engine event loop - runs on tokio outside Bevy
use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use uuid::Uuid;
use super::{EngineCommand, EngineEvent, EngineHandle, NetworkingManager, PersistenceManager};
@@ -9,6 +10,7 @@ use crate::networking::{SessionId, VectorClock};
pub struct EngineCore {
handle: EngineHandle,
networking_task: Option<JoinHandle<()>>,
networking_cancel_token: Option<CancellationToken>,
#[allow(dead_code)]
persistence: PersistenceManager,
@@ -28,6 +30,7 @@ impl EngineCore {
Self {
handle,
networking_task: None, // Start offline
networking_cancel_token: None,
persistence,
node_id,
clock,
@@ -93,39 +96,73 @@ impl EngineCore {
return;
}
match NetworkingManager::new(session_id.clone()).await {
Ok((net_manager, bridge)) => {
let node_id = net_manager.node_id();
tracing::info!("Starting networking initialization for session {}", session_id.to_code());
// Spawn NetworkingManager in background task
let event_tx = self.handle.event_tx.clone();
let task = tokio::spawn(async move {
net_manager.run(event_tx).await;
// Create cancellation token for graceful shutdown
let cancel_token = CancellationToken::new();
let cancel_token_clone = cancel_token.clone();
// Spawn NetworkingManager initialization in background to avoid blocking
// DHT peer discovery can take 15+ seconds with retries
let event_tx = self.handle.event_tx.clone();
// Create channel for progress updates
let (progress_tx, mut progress_rx) = tokio::sync::mpsc::unbounded_channel();
// Spawn task to forward progress updates to Bevy
let event_tx_clone = event_tx.clone();
let session_id_clone = session_id.clone();
tokio::spawn(async move {
while let Some(status) = progress_rx.recv().await {
let _ = event_tx_clone.send(EngineEvent::NetworkingInitializing {
session_id: session_id_clone.clone(),
status,
});
self.networking_task = Some(task);
let _ = self.handle.event_tx.send(EngineEvent::NetworkingStarted {
session_id: session_id.clone(),
node_id,
bridge,
});
tracing::info!("Networking started for session {}", session_id.to_code());
}
Err(e) => {
let _ = self.handle.event_tx.send(EngineEvent::NetworkingFailed {
error: e.to_string(),
});
tracing::error!("Failed to start networking: {}", e);
});
let task = tokio::spawn(async move {
match NetworkingManager::new(session_id.clone(), Some(progress_tx), cancel_token_clone.clone()).await {
Ok((net_manager, bridge)) => {
let node_id = net_manager.node_id();
// Notify Bevy that networking started
let _ = event_tx.send(EngineEvent::NetworkingStarted {
session_id: session_id.clone(),
node_id,
bridge,
});
tracing::info!("Networking started for session {}", session_id.to_code());
// Run the networking manager loop with cancellation support
net_manager.run(event_tx.clone(), cancel_token_clone).await;
}
Err(e) => {
let _ = event_tx.send(EngineEvent::NetworkingFailed {
error: e.to_string(),
});
tracing::error!("Failed to start networking: {}", e);
}
}
}
});
self.networking_task = Some(task);
self.networking_cancel_token = Some(cancel_token);
}
async fn stop_networking(&mut self) {
// Cancel the task gracefully
if let Some(cancel_token) = self.networking_cancel_token.take() {
cancel_token.cancel();
tracing::info!("Networking cancellation requested");
}
// Abort the task immediately - don't wait for graceful shutdown
// This is fine because NetworkingManager doesn't hold critical resources
if let Some(task) = self.networking_task.take() {
task.abort(); // Cancel the networking task
task.abort();
tracing::info!("Networking task aborted");
let _ = self.handle.event_tx.send(EngineEvent::NetworkingStopped);
tracing::info!("Networking stopped");
}
}