97 lines
2.7 KiB
Rust
97 lines
2.7 KiB
Rust
#[macro_use]
|
|
extern crate log;
|
|
|
|
use anyhow::{Context, Error, Result};
|
|
use future::ok;
|
|
use futures::prelude::*;
|
|
use net::{ClientMessage, Meta, ServerMessage};
|
|
use serde_json::{from_str, json, to_string, Value};
|
|
use std::net::ToSocketAddrs;
|
|
use stream::{iter, FuturesUnordered};
|
|
use structopt::StructOpt;
|
|
use warp::{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();
|
|
|
|
// dispatch websockets
|
|
let socket_handler = ws().map(|upgrade: Ws| upgrade.on_upgrade(handle_socket));
|
|
|
|
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(socket_handler).try_bind_ephemeral(addr)?;
|
|
servers.push(server);
|
|
}
|
|
|
|
servers.for_each(|_| async {}).await;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn handle_socket(websocket: WebSocket) {
|
|
let (sink, mut source) = websocket.split();
|
|
|
|
let mut sink = sink.with(|msg: ServerMessage<Vec<Value>, Value>| {
|
|
let json = to_string(&msg).expect("JSON encoding shouldn't fail");
|
|
ok::<Message, Error>(Message::text(json))
|
|
});
|
|
|
|
let mut greeting = iter(vec![
|
|
ServerMessage::Meta {
|
|
m: Meta {
|
|
version: "Unstable",
|
|
helo: Some("Dedicated base2020 server".into()),
|
|
},
|
|
},
|
|
ServerMessage::SetState {
|
|
u: 0,
|
|
s: json!({}),
|
|
},
|
|
])
|
|
.map(Ok);
|
|
|
|
if let Err(err) = sink.send_all(&mut greeting).await {
|
|
warn!("Websocket send error: {:#}", err);
|
|
return;
|
|
}
|
|
|
|
let error: Option<Error> = loop {
|
|
match source.next().await {
|
|
Some(Ok(msg)) => match msg.to_str() {
|
|
Ok(json) => {
|
|
match from_str::<ClientMessage<Value, Value>>(json).context("Parsing JSON") {
|
|
Ok(msg) => {
|
|
debug!("Client message: {:?}", &msg);
|
|
}
|
|
Err(error) => break Some(error),
|
|
}
|
|
}
|
|
Err(()) => debug!("Non-text message"),
|
|
},
|
|
Some(Err(error)) => break Some(error.into()),
|
|
None => break None,
|
|
}
|
|
};
|
|
if let Some(error) = error {
|
|
warn!("Websocket connection lost: {:#}", error);
|
|
}
|
|
}
|