SeaBattle/rust/sea_battle_backend/src/dispatcher_actor.rs
2022-10-16 18:06:31 +02:00

146 lines
3.9 KiB
Rust

//! # 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::string_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<HumanPlayerWS>);
#[derive(Message)]
#[rtype(result = "()")]
pub struct AcceptInvite(pub String, pub Addr<HumanPlayerWS>);
#[derive(Message)]
#[rtype(result = "()")]
pub struct PlayRandom(pub Addr<HumanPlayerWS>);
#[derive(Debug, Clone)]
struct PendingPlayer {
player: Addr<HumanPlayerWS>,
rules: GameRules,
}
#[derive(Default)]
pub struct DispatcherActor {
with_invite: HashMap<String, PendingPlayer>,
random_player: Option<PendingPlayer>,
}
impl DispatcherActor {
/// Run garbage collector
fn run_gc(&mut self) {
// Garbage collect invites
let ids = self
.with_invite
.iter()
.filter(|p| !p.1.player.connected())
.map(|p| p.0.clone())
.collect::<Vec<_>>();
for id in ids {
log::debug!("Remove dead invite: {}", id);
self.with_invite.remove(&id);
}
// Garbage collect random player
if let Some(player) = self.random_player.clone() {
if !player.player.connected() {
self.random_player = None;
}
}
}
}
impl Actor for DispatcherActor {
type Context = Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
ctx.run_interval(HEARTBEAT_INTERVAL, |act, _ctx| {
act.run_gc();
});
}
}
impl Handler<CreateInvite> 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(),
});
self.with_invite.insert(
invite_code,
PendingPlayer {
player: msg.1,
rules: msg.0,
},
);
}
}
impl Handler<AcceptInvite> for DispatcherActor {
type Result = ();
fn handle(&mut self, msg: AcceptInvite, _ctx: &mut Self::Context) -> Self::Result {
self.run_gc();
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));
}
}
impl Handler<PlayRandom> for DispatcherActor {
type Result = ();
fn handle(&mut self, msg: PlayRandom, _ctx: &mut Self::Context) -> Self::Result {
self.run_gc();
match self.random_player.take() {
None => {
self.random_player = Some(PendingPlayer {
player: msg.0,
rules: GameRules::random_players_rules(),
});
}
Some(p) => {
let game = Game::new(p.rules).start();
p.player.do_send(SetGame(game.clone()));
msg.0.do_send(SetGame(game));
self.random_player = None;
}
}
}
}