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};
|
2022-10-01 18:44:01 +00:00
|
|
|
use crate::utils::string_utils::rand_str;
|
2022-09-24 09:46:12 +00:00
|
|
|
|
|
|
|
/// 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>);
|
|
|
|
|
2022-10-01 08:16:00 +00:00
|
|
|
#[derive(Message)]
|
|
|
|
#[rtype(result = "()")]
|
|
|
|
pub struct PlayRandom(pub Addr<HumanPlayerWS>);
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
2022-09-24 09:46:12 +00:00
|
|
|
struct PendingPlayer {
|
|
|
|
player: Addr<HumanPlayerWS>,
|
|
|
|
rules: GameRules,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct DispatcherActor {
|
|
|
|
with_invite: HashMap<String, PendingPlayer>,
|
2022-10-01 08:16:00 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-24 09:46:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Actor for DispatcherActor {
|
|
|
|
type Context = Context<Self>;
|
|
|
|
|
|
|
|
fn started(&mut self, ctx: &mut Self::Context) {
|
|
|
|
ctx.run_interval(HEARTBEAT_INTERVAL, |act, _ctx| {
|
2022-10-01 08:16:00 +00:00
|
|
|
act.run_gc();
|
2022-09-24 09:46:12 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2022-10-01 08:16:00 +00:00
|
|
|
self.run_gc();
|
|
|
|
|
2022-09-24 09:46:12 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
2022-10-01 08:16:00 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|