SeaBattle/sea_battle_backend/src/dispatcher_actor.rs

101 lines
2.8 KiB
Rust
Raw Normal View History

2022-09-24 09:46:12 +00:00
//! # 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<HumanPlayerWS>);
#[derive(Message)]
#[rtype(result = "()")]
pub struct AcceptInvite(pub String, pub Addr<HumanPlayerWS>);
struct PendingPlayer {
player: Addr<HumanPlayerWS>,
rules: GameRules,
}
#[derive(Default)]
pub struct DispatcherActor {
with_invite: HashMap<String, PendingPlayer>,
}
impl Actor for DispatcherActor {
type Context = Context<Self>;
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::<Vec<_>>();
for id in ids {
log::debug!("Remove dead invite: {}", id);
act.with_invite.remove(&id);
}
});
}
}
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(),
});
msg.1.do_send(ServerMessage::WaitingForAnotherPlayer);
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 {
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));
}
}