diff --git a/Cargo.lock b/Cargo.lock index d0d147c..e7d9d0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,9 +122,11 @@ checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" name = "carrier-signal" version = "0.1.0" dependencies = [ + "futures", "legion", "rand 0.7.3", "serde", + "serde_json", "tera", "tokio", "warp", diff --git a/Cargo.toml b/Cargo.toml index 0c1e5b0..59dee60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,11 @@ license = "AGPL-3.0-or-later" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +futures = { version = "0.3", default-features = false, features = ["alloc"] } legion = "0.2.1" rand = "0.7.3" serde = { version = "1.0", features= ["derive"] } +serde_json = "1.0" tera = "1" tokio = { version = "0.2", features = ["full"] } warp = "0.2" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c801732..e462cb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ mod simulation; mod state; -use std::convert::Infallible; -use std::sync::Arc; +use std::{convert::Infallible, sync::Arc}; +use futures::SinkExt; +use serde_json::json; use tera::{Context, Tera}; use tokio::sync::watch; -use warp::Filter; +use warp::{Filter, ws::{Message, WebSocket}}; use simulation::Simulation; use state::State; @@ -29,26 +30,58 @@ async fn main() { } }; let tr = Arc::new(tr); - let tera = move |with_template| render(with_template, tr.clone()); + let tera = move || { + let tr = tr.clone(); + move |with_template| render(with_template, tr.clone()) + }; + let state = move || { + let state_channel = state_channel.clone(); + move || state_channel.clone() + }; let sim = tokio::spawn(async move { simulation.run().await; }); - let hello = warp::get().and(warp::path::end()) - .map(move || (state_channel.clone())) + let hello = warp::get() + .and(warp::path::end()) + .map(state()) .and_then(|channel| async move { get_state(channel).await }) .map(|state| WithTemplate { name: "stuff.tera", - context: Context::from_serialize(state).unwrap() + context: Context::from_serialize(state).unwrap(), }) - .map(tera); + .map(tera()); - let server = tokio::spawn(async move { warp::serve(hello).run(([127, 0, 0, 1], 3030)).await }); + let wsstate = state(); + let ws = warp::path("state") + .and(warp::ws()) + .map(move |ws: warp::ws::Ws| (ws, wsstate())) + .map(|(ws, state): (warp::ws::Ws, watch::Receiver)| { + ws.on_upgrade(move |websocket| watch_state(websocket, state)) + }); + + let server = + tokio::spawn(async move { warp::serve(hello.or(ws)).run(([127, 0, 0, 1], 3030)).await }); tokio::try_join!(sim, server).unwrap(); } +async fn watch_state(mut ws: WebSocket, mut state_channel: watch::Receiver) { + loop { + let state = state_channel.recv().await; + + let result = match state { + Some(state) => ws.send(Message::text(json!(state).to_string())).await, + None => ws.send(Message::text("Oh...")).await, + }; + + if let Err(_) = result { + break; + } + }; +} + async fn get_state(mut channel: watch::Receiver) -> Result { Ok(channel.recv().await.unwrap()) } diff --git a/templates/stuff.tera b/templates/stuff.tera index 7f5476d..3f5277b 100644 --- a/templates/stuff.tera +++ b/templates/stuff.tera @@ -3,6 +3,7 @@ Blab +

Hello, world!

    @@ -10,5 +11,18 @@
  • {{ object.name }} @ {{ object.x }}, {{ object.y }}
  • {% endfor %}
+ + +