//! # Dispatcher actors //! //! Allows to establish connections between human players use std::collections::HashMap; use std::time::Duration; use actix::{Actor, Addr, AsyncContext, Context, Handler, Message}; use crate::consts::INVITE_CODE_LENGTH; use crate::data::GameRules; use crate::game::Game; use crate::human_player_ws::{CloseConnection, HumanPlayerWS, ServerMessage, SetGame}; use crate::utils::rand_str; /// How often garbage collector is run const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(60); #[derive(Message)] #[rtype(result = "()")] pub struct CreateInvite(pub GameRules, pub Addr); #[derive(Message)] #[rtype(result = "()")] pub struct AcceptInvite(pub String, pub Addr); struct PendingPlayer { player: Addr, rules: GameRules, } #[derive(Default)] pub struct DispatcherActor { with_invite: HashMap, } impl Actor for DispatcherActor { type Context = Context; fn started(&mut self, ctx: &mut Self::Context) { ctx.run_interval(HEARTBEAT_INTERVAL, |act, _ctx| { // Garbage collect invites let ids = act .with_invite .iter() .filter(|p| !p.1.player.connected()) .map(|p| p.0.clone()) .collect::>(); for id in ids { log::debug!("Remove dead invite: {}", id); act.with_invite.remove(&id); } }); } } impl Handler for DispatcherActor { type Result = (); fn handle(&mut self, msg: CreateInvite, _ctx: &mut Self::Context) -> Self::Result { let mut invite_code = rand_str(INVITE_CODE_LENGTH).to_uppercase(); while self.with_invite.contains_key(&invite_code) { invite_code = rand_str(INVITE_CODE_LENGTH).to_uppercase(); } log::debug!("Insert new invitation: {}", invite_code); msg.1.do_send(ServerMessage::SetInviteCode { code: invite_code.clone(), }); msg.1.do_send(ServerMessage::WaitingForAnotherPlayer); self.with_invite.insert( invite_code, PendingPlayer { player: msg.1, rules: msg.0, }, ); } } impl Handler for DispatcherActor { type Result = (); fn handle(&mut self, msg: AcceptInvite, _ctx: &mut Self::Context) -> Self::Result { let entry = match self.with_invite.remove(&msg.0) { None => { msg.1.do_send(ServerMessage::InvalidInviteCode); msg.1.do_send(CloseConnection); return; } Some(e) => e, }; let game = Game::new(entry.rules).start(); entry.player.do_send(SetGame(game.clone())); msg.1.do_send(SetGame(game)); } }