use actix::fut;
use actix::ActorContext;
use actix::ActorFutureExt;
use actix::{Actor, Addr, ContextFutureSpawner, Running, StreamHandler, WrapFuture};
use actix::{AsyncContext, Handler};
use actix_web_actors::ws;
use actix_web_actors::ws::Message::Text;
use std::time::{Duration, Instant};
use uuid::Uuid;
use crate::websocket::lobbies::lobby::Lobby;
use crate::websocket::lobbies::messages::{ClientActorMessage, Connect, Disconnect, WsMessage};
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
pub struct WsConn {
room: Uuid,
lobby_addr: Addr<Lobby>,
hb: Instant,
id: Uuid,
}
impl WsConn {
pub fn new(room: Uuid, lobby: Addr<Lobby>) -> WsConn {
WsConn {
id: Uuid::now_v7(),
room,
hb: Instant::now(),
lobby_addr: lobby,
}
}
}
impl WsConn {
fn hb(&self, ctx: &mut ws::WebsocketContext<Self>) {
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
log::warn!("Disconnecting failed heartbeat");
ctx.stop();
return;
}
ctx.ping(b"hi");
});
}
}
impl Handler<WsMessage> for WsConn {
type Result = ();
fn handle(&mut self, msg: WsMessage, ctx: &mut Self::Context) {
ctx.text(msg.0);
}
}
impl Actor for WsConn {
type Context = ws::WebsocketContext<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
self.hb(ctx);
let addr = ctx.address();
self.lobby_addr
.send(
Connect {
addr: addr.recipient(),
lobby_id: self.room,
self_id: self.id,
},
)
.into_actor(self)
.then(|res, _, ctx| {
match res {
Ok(_res) => (),
_ => ctx.stop(),
}
fut::ready(())
})
.wait(ctx);
}
fn stopping(&mut self, _: &mut Self::Context) -> Running {
self.lobby_addr.do_send(
Disconnect {
id: self.id,
room_id: self.room,
},
);
Running::Stop
}
}
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsConn {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
match msg {
Ok(ws::Message::Ping(msg)) => {
self.hb = Instant::now();
ctx.pong(&msg);
}
Ok(ws::Message::Pong(_)) => {
self.hb = Instant::now();
}
Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
Ok(ws::Message::Close(reason)) => {
ctx.close(reason);
ctx.stop();
}
Ok(ws::Message::Continuation(_)) => {
ctx.stop();
}
Ok(ws::Message::Nop) => (),
Ok(Text(s)) => self.lobby_addr.do_send(
ClientActorMessage {
id: self.id,
msg: s.to_string(),
room_id: self.room,
},
),
Err(e) => {
log::error!("error={:?}", e);
}
}
}
}