First test kinda working
This commit is contained in:
@@ -1,68 +1,111 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy::state::app::StatesPlugin;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::sync::mpsc;
|
||||
use crate::transport::QuicMessage;
|
||||
use crate::transport::server::run_substrate_server;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::transport::{QuicMessage, T1Sender, T2Sender, T3Inbound, T3Sender};
|
||||
use crate::transport::server::{accept_loop, bind_endpoint};
|
||||
use crate::transport::state::ServerState;
|
||||
|
||||
const T1_CAPACITY: usize = 1024;
|
||||
const T2_CAPACITY: usize = 512;
|
||||
const T3_CAPACITY: usize = 256;
|
||||
|
||||
pub struct EcsQuicTransportPlugin{}
|
||||
pub struct EcsQuicTransportPlugin;
|
||||
|
||||
/// Receive halves of the three tier channels, wrapped so they can sit in a
|
||||
/// Bevy `Resource`. The `world` module's ingest system is the sole reader.
|
||||
#[derive(Resource)]
|
||||
struct BridgeReceivers {
|
||||
t1: Mutex<mpsc::Receiver<QuicMessage>>,
|
||||
t2: Mutex<mpsc::Receiver<QuicMessage>>,
|
||||
t3: Mutex<mpsc::Receiver<QuicMessage>>,
|
||||
pub(crate) struct BridgeReceivers {
|
||||
pub(crate) t1: Mutex<mpsc::Receiver<QuicMessage>>,
|
||||
pub(crate) t2: Mutex<mpsc::Receiver<QuicMessage>>,
|
||||
pub(crate) t3: Mutex<mpsc::Receiver<T3Inbound>>,
|
||||
}
|
||||
|
||||
fn ingest_system(bridge: Res<BridgeReceivers>){
|
||||
let mut t1 = bridge.t1.lock().unwrap();
|
||||
// Tier 1: drain up to N messages, drop the rest
|
||||
for _ in 0..T1_CAPACITY {
|
||||
match t1.try_recv() {
|
||||
Ok(msg) => { /* write RawSensorData */ }
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
|
||||
// T2/T3: drain completely, these are low volume
|
||||
let mut t2 = bridge.t2.lock().unwrap();
|
||||
while let Ok(msg) = t2.try_recv() { /* ... */ }
|
||||
|
||||
let mut t3 = bridge.t3.lock().unwrap();
|
||||
while let Ok(msg) = t3.try_recv() { /* ... */ }
|
||||
#[derive(Resource, Clone)]
|
||||
pub(crate) struct BridgeSenders {
|
||||
pub(crate) t1: T1Sender,
|
||||
pub(crate) t2: T2Sender,
|
||||
pub(crate) t3: T3Sender,
|
||||
}
|
||||
|
||||
impl Plugin for EcsQuicTransportPlugin{
|
||||
#[derive(Resource, Clone)]
|
||||
pub(crate) struct TokioHandle(pub(crate) Handle);
|
||||
|
||||
/// Bring up the QUIC listener using the loaded `AppConfig` and transition to
|
||||
/// `ServerState::Started`. Runs once via `OnEnter(ServerState::Starting)`.
|
||||
fn start_quic_server(
|
||||
config: Res<AppConfig>,
|
||||
senders: Res<BridgeSenders>,
|
||||
runtime: Res<TokioHandle>,
|
||||
mut next: ResMut<NextState<ServerState>>,
|
||||
) {
|
||||
tracing::info!("entering ServerState::Starting — bringing up QUIC listener");
|
||||
|
||||
// `Endpoint::server` is sync but needs a tokio runtime context for
|
||||
// `Handle::current()`; entering the runtime is enough — no async block
|
||||
// required.
|
||||
let _guard = runtime.0.enter();
|
||||
let endpoint = bind_endpoint(&config.network).expect("failed to bind QUIC endpoint");
|
||||
drop(_guard);
|
||||
|
||||
tracing::info!(local = ?endpoint.local_addr().ok(), "QUIC listener bound");
|
||||
|
||||
let s = senders.clone();
|
||||
runtime.0.spawn(accept_loop(endpoint, s.t1, s.t2, s.t3));
|
||||
|
||||
next.set(ServerState::Started);
|
||||
tracing::info!("ServerState::Started");
|
||||
}
|
||||
|
||||
impl Plugin for EcsQuicTransportPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
// Create the channels for multi-thread communication
|
||||
let (t1_tx, t1_rx) =
|
||||
mpsc::channel::<QuicMessage>(T1_CAPACITY);
|
||||
let (t2_tx, t2_rx) =
|
||||
mpsc::channel::<QuicMessage>(T2_CAPACITY);
|
||||
let (t3_tx, t3_rx) =
|
||||
mpsc::channel::<QuicMessage>(T3_CAPACITY);
|
||||
// Three-tier bridge between the tokio-side QUIC accept loop and the
|
||||
// ECS PreUpdate ingest system (in the `world` module).
|
||||
let (t1_tx, t1_rx) = mpsc::channel::<QuicMessage>(T1_CAPACITY);
|
||||
let (t2_tx, t2_rx) = mpsc::channel::<QuicMessage>(T2_CAPACITY);
|
||||
let (t3_tx, t3_rx) = mpsc::channel::<T3Inbound>(T3_CAPACITY);
|
||||
|
||||
let quic_handle = std::thread::spawn(move || {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(2)
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
// Spawn a tokio runtime on a dedicated OS thread, ship its Handle back
|
||||
// to the ECS, and keep the runtime alive for the lifetime of the app
|
||||
// by parking on `pending()`.
|
||||
let (handle_tx, handle_rx) = std::sync::mpsc::sync_channel::<Handle>(1);
|
||||
std::thread::Builder::new()
|
||||
.name("quic-runtime".to_string())
|
||||
.spawn(move || {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(2)
|
||||
.enable_all()
|
||||
.thread_name("quic-worker")
|
||||
.build()
|
||||
.expect("build tokio runtime");
|
||||
handle_tx
|
||||
.send(rt.handle().clone())
|
||||
.expect("send tokio Handle to ECS");
|
||||
rt.block_on(std::future::pending::<()>());
|
||||
})
|
||||
.expect("spawn quic-runtime thread");
|
||||
|
||||
rt.block_on(async move {
|
||||
run_substrate_server(t1_tx, t2_tx, t3_tx).await;
|
||||
});
|
||||
});
|
||||
let handle = handle_rx.recv().expect("receive tokio Handle");
|
||||
|
||||
app.insert_resource(BridgeReceivers {
|
||||
t1: Mutex::new(t1_rx),
|
||||
t2: Mutex::new(t2_rx),
|
||||
t3: Mutex::new(t3_rx),
|
||||
});
|
||||
|
||||
app.add_systems(PreUpdate, ingest_system);
|
||||
// Bevy 0.18 split state machinery into its own plugin; under
|
||||
// MinimalPlugins it isn't installed by default.
|
||||
app.add_plugins(StatesPlugin)
|
||||
.init_state::<ServerState>()
|
||||
.insert_resource(TokioHandle(handle))
|
||||
.insert_resource(BridgeSenders {
|
||||
t1: T1Sender::new(t1_tx),
|
||||
t2: T2Sender::new(t2_tx),
|
||||
t3: T3Sender::new(t3_tx),
|
||||
})
|
||||
.insert_resource(BridgeReceivers {
|
||||
t1: Mutex::new(t1_rx),
|
||||
t2: Mutex::new(t2_rx),
|
||||
t3: Mutex::new(t3_rx),
|
||||
})
|
||||
.add_systems(OnEnter(ServerState::Starting), start_quic_server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user