|
@@ -0,0 +1,124 @@
|
|
|
+use std::time::{Duration, Instant};
|
|
|
+use std::collections::BTreeMap;
|
|
|
+use std::sync::Mutex;
|
|
|
+
|
|
|
+use serde::{Serialize, Deserialize};
|
|
|
+
|
|
|
+use actix::prelude::*;
|
|
|
+use actix_web::{web, Error, HttpRequest, HttpResponse};
|
|
|
+use actix_web_actors::ws;
|
|
|
+
|
|
|
+use crate::game;
|
|
|
+
|
|
|
+const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
|
|
+const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
|
|
|
+
|
|
|
+pub async fn ws_initiate(map: web::Data<Mutex<BTreeMap<String, game::Game>>>, r: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
|
|
|
+ println!("{:?}", r);
|
|
|
+ let res = ws::start(WebSock::new(map.clone()), &r, stream);
|
|
|
+ println!("{:?}", res);
|
|
|
+ res
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Serialize, Deserialize)]
|
|
|
+#[serde(rename_all = "lowercase")]
|
|
|
+enum Message {
|
|
|
+ Join{ game_id: String, nick: String },
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Serialize, Deserialize)]
|
|
|
+#[serde(rename_all = "lowercase")]
|
|
|
+enum UpdateMessage {
|
|
|
+ GameState (game::Game)
|
|
|
+}
|
|
|
+
|
|
|
+struct WebSock {
|
|
|
+ heartbeat: Instant,
|
|
|
+ nick: Option<String>,
|
|
|
+ game_id: Option<String>,
|
|
|
+ server_data: web::Data<Mutex<BTreeMap<String, game::Game>>>,
|
|
|
+}
|
|
|
+
|
|
|
+impl Actor for WebSock {
|
|
|
+ type Context = ws::WebsocketContext<Self>;
|
|
|
+
|
|
|
+ fn started(&mut self, ctx: &mut Self::Context) {
|
|
|
+ self.initiate_heartbeat(ctx);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSock {
|
|
|
+ fn handle(
|
|
|
+ &mut self,
|
|
|
+ msg: Result<ws::Message, ws::ProtocolError>,
|
|
|
+ ctx: &mut Self::Context,
|
|
|
+ ) {
|
|
|
+ // process websocket messages
|
|
|
+ println!("WS: {:?}", msg);
|
|
|
+ match msg {
|
|
|
+ Ok(ws::Message::Ping(msg)) => {
|
|
|
+ self.heartbeat = Instant::now();
|
|
|
+ ctx.pong(&msg);
|
|
|
+ }
|
|
|
+ Ok(ws::Message::Pong(_)) => {
|
|
|
+ self.heartbeat = Instant::now();
|
|
|
+ }
|
|
|
+ Ok(ws::Message::Text(text)) => {
|
|
|
+ self.handle_message(&text, ctx);
|
|
|
+ },
|
|
|
+ Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
|
|
|
+ Ok(ws::Message::Close(reason)) => {
|
|
|
+ ctx.close(reason);
|
|
|
+ ctx.stop();
|
|
|
+ }
|
|
|
+ _ => ctx.stop(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl WebSock {
|
|
|
+ pub fn new(map: web::Data<Mutex<BTreeMap<String, game::Game>>>) -> Self {
|
|
|
+ Self { heartbeat: Instant::now(), nick: None, game_id: None, server_data: map }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn initiate_heartbeat(&self, ctx: &mut <Self as Actor>::Context) {
|
|
|
+ ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
|
|
|
+ if Instant::now().duration_since(act.heartbeat) > CLIENT_TIMEOUT {
|
|
|
+ //println!("Websocket Client heartbeat failed, disconnecting!");
|
|
|
+ ctx.stop();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ctx.ping(b"");
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn handle_message(&mut self, text: &str, ctx: &mut <WebSock as Actor>::Context) {
|
|
|
+ let x = Message::Join{ game_id: "gmae".to_owned(), nick: "name".to_owned() };
|
|
|
+ let string = serde_json::to_string(&x);
|
|
|
+ println!("{}", string.unwrap());
|
|
|
+
|
|
|
+
|
|
|
+ let parsed: Result<Message, _> = serde_json::from_str(text);
|
|
|
+ if let Ok(msg) = parsed {
|
|
|
+ match msg {
|
|
|
+ Message::Join{game_id, nick} => {
|
|
|
+ println!("just joined: {}, {}", game_id, nick);
|
|
|
+ self.game_id = Some(game_id.clone());
|
|
|
+ self.nick = Some(nick);
|
|
|
+ let mut map = self.server_data.lock().unwrap();
|
|
|
+ match map.get(&game_id) {
|
|
|
+ Some(game) => {
|
|
|
+ let txt = serde_json::to_string(game).unwrap();
|
|
|
+ println!("{:?}", txt);
|
|
|
+ ctx.text(txt);
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ map.insert(game_id, game::Game::new());
|
|
|
+ println!("game not found");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|