|
|
|
@ -1,3 +1,4 @@
|
|
|
|
|
use crate::leaderboard::{InMemoryLeaderboard, Leaderboard};
|
|
|
|
|
use crate::player::Player;
|
|
|
|
|
use board_network::protocol::{ClientMessage, ServerMessage};
|
|
|
|
|
use board_network::types::TileRef;
|
|
|
|
@ -9,16 +10,17 @@ use board_shared::position::Position2d;
|
|
|
|
|
use board_shared::score::calc_score;
|
|
|
|
|
use board_shared::tile::Tile;
|
|
|
|
|
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
|
|
|
|
use futures::executor::block_on;
|
|
|
|
|
use futures::StreamExt;
|
|
|
|
|
use rand::distributions::{Alphanumeric, DistString};
|
|
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::fmt;
|
|
|
|
|
use std::net::SocketAddr;
|
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
|
|
|
|
|
type TaggedClientMessage = (SocketAddr, ClientMessage);
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
|
pub struct Room {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub connections: HashMap<SocketAddr, usize>,
|
|
|
|
@ -28,9 +30,18 @@ pub struct Room {
|
|
|
|
|
pub board: Board,
|
|
|
|
|
pub validated_board: Board,
|
|
|
|
|
pub deck: RngDeck,
|
|
|
|
|
pub leaderboard: Arc<dyn Leaderboard>,
|
|
|
|
|
pub successive_skipped_turns: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Room {
|
|
|
|
|
pub fn with_leaderboard(leaderboard: Arc<dyn Leaderboard>) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
leaderboard,
|
|
|
|
|
..Self::default()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_player(
|
|
|
|
|
&mut self,
|
|
|
|
|
addr: SocketAddr,
|
|
|
|
@ -87,6 +98,11 @@ impl Room {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.successive_skipped_turns >= self.players.len() {
|
|
|
|
|
self.on_game_over();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
self.active_player = (self.active_player + 1) % self.players.len();
|
|
|
|
|
if self.players[self.active_player].ws.is_some() {
|
|
|
|
@ -171,6 +187,10 @@ impl Room {
|
|
|
|
|
|
|
|
|
|
fn on_validate(&mut self) {
|
|
|
|
|
let diff = self.board.difference(&self.validated_board);
|
|
|
|
|
if diff.is_empty() {
|
|
|
|
|
self.successive_skipped_turns += 1;
|
|
|
|
|
self.next_player();
|
|
|
|
|
}
|
|
|
|
|
if !Board::has_alignment(&diff) {
|
|
|
|
|
self.reset_player_moves();
|
|
|
|
|
self.send(
|
|
|
|
@ -215,6 +235,12 @@ impl Room {
|
|
|
|
|
self.players[player_id].score,
|
|
|
|
|
));
|
|
|
|
|
self.validated_board = self.board.clone();
|
|
|
|
|
self.successive_skipped_turns = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_game_over(&mut self) {
|
|
|
|
|
self.broadcast(ServerMessage::GameOver);
|
|
|
|
|
block_on(self.update_global_leaderboard());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn reset_player_moves(&mut self) {
|
|
|
|
@ -226,6 +252,15 @@ impl Room {
|
|
|
|
|
self.sync_hand(self.active_player);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn update_global_leaderboard(&mut self) {
|
|
|
|
|
for player in &self.players {
|
|
|
|
|
self.leaderboard
|
|
|
|
|
.add_score(&player.name, player.score)
|
|
|
|
|
.await
|
|
|
|
|
.ok();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_client_disconnected(&mut self, addr: SocketAddr) {
|
|
|
|
|
if let Some(p) = self.connections.remove(&addr) {
|
|
|
|
|
self.players[p].ws = None;
|
|
|
|
@ -273,6 +308,39 @@ impl Room {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Room {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
name: String::new(),
|
|
|
|
|
connections: Default::default(),
|
|
|
|
|
players: vec![],
|
|
|
|
|
active_player: 0,
|
|
|
|
|
has_started: false,
|
|
|
|
|
board: Default::default(),
|
|
|
|
|
validated_board: Default::default(),
|
|
|
|
|
deck: Default::default(),
|
|
|
|
|
leaderboard: Arc::new(InMemoryLeaderboard::default()),
|
|
|
|
|
successive_skipped_turns: 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Debug for Room {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
f.debug_struct("Room")
|
|
|
|
|
.field("name", &self.name)
|
|
|
|
|
.field("connections", &self.connections)
|
|
|
|
|
.field("players", &self.players)
|
|
|
|
|
.field("active_player", &self.active_player)
|
|
|
|
|
.field("has_started", &self.has_started)
|
|
|
|
|
.field("board", &self.board)
|
|
|
|
|
.field("validated_board", &self.validated_board)
|
|
|
|
|
.field("deck", &self.deck)
|
|
|
|
|
.field("skipped_successive_turns", &self.successive_skipped_turns)
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type RoomPtr = Arc<Mutex<Room>>;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|