base2020/src/main.rs

92 lines
2.5 KiB
Rust

#[macro_use]
extern crate log;
use anyhow::{Context, Result};
use future::ready;
use futures::prelude::*;
use net::{
server::{run_client, Handle, Server},
ClientMessage, ServerMessage,
};
use serde_json::{from_str, to_string};
use std::net::ToSocketAddrs;
use stream::FuturesUnordered;
use structopt::StructOpt;
use warp::{path, serve, ws, ws::Ws, Filter};
use ws::{Message, WebSocket};
pub mod net;
#[derive(StructOpt)]
/// Server for base2020 lockstep protocol for multiplayer games.
struct Args {
/// The socket address to listen for connections on;
/// can be a hostname to bind to multiple hosts at once,
/// such as to listen on both IPv4 & IPv6.
listen: String,
}
#[tokio::main]
async fn main() -> Result<()> {
env_logger::init();
let args = Args::from_args();
// create singleton server (use weak-table in the future for multiple rooms)
let game_server = Server::create();
// dispatch websockets
let socket_handler = ws().map(move |upgrade: Ws| {
let handle = game_server.clone();
upgrade.on_upgrade(move |ws| {
async {
if let Err(error) = handle_socket(handle, ws).await {
warn!("Websocket connection lost: {:#}", error);
}
}
})
});
// assemble routes
let routes = path!("base2020.ws").and(socket_handler);
let addrs = args
.listen
.to_socket_addrs()
.context("Couldn't parse the listen address")?;
let servers = FuturesUnordered::new();
for addr in addrs {
let (_, server) = serve(routes.clone()).try_bind_ephemeral(addr)?;
servers.push(server);
}
servers.for_each(|_| async {}).await;
Ok(())
}
async fn handle_socket(game_server: Handle, websocket: WebSocket) -> Result<()> {
let (sink, source) = websocket.split();
let mut sink = sink.with(|msg: ServerMessage| {
ready(
to_string(&msg)
.context("JSON encoding shouldn't fail")
.map(|json| Message::text(json)),
)
});
let mut source = source.map_err(Into::into).try_filter_map(|msg| {
ready(match msg.to_str() {
Ok(json) => from_str::<ClientMessage>(json)
.context("Parsing JSON")
.map(Some),
Err(()) => {
debug!("Non-text message {:?}", &msg);
Ok(None)
}
})
});
run_client(game_server, &mut source, &mut sink).await
}