Skip to content

Commit

Permalink
the lobby is waaaaaay cleaner, but it's also the only thing that works
Browse files Browse the repository at this point in the history
  • Loading branch information
Rayzeq committed Dec 18, 2023
1 parent 0331e66 commit feed6fa
Show file tree
Hide file tree
Showing 7 changed files with 551 additions and 212 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ edition = "2021"
[dependencies]
rand = "0.8.5"
rocket = { version = "0.5.0", features = ["secrets", "json"] }
thiserror = "1.0.51"
51 changes: 51 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
pub use crate::PlayerWrapper;
use crate::{
game::{Game, Lobby, Player, Room},
lobby,
};
use std::{
collections::HashMap,
marker::PhantomData,
sync::{Arc, Mutex, MutexGuard},
};

macro_rules! make_event {
($message:expr) => {{
let __msg = $message;
Event::json(&__msg).event(__msg.name())
}};
}

pub(crate) use make_event;

pub struct GlobalState {
pub lobbys: Mutex<HashMap<String, Protected<lobby::Player, Lobby<lobby::Player>>>>,
pub games: Mutex<HashMap<String, Protected<crate::PlayerWrapper, Game<crate::PlayerWrapper>>>>,
}

impl GlobalState {
pub fn new() -> Self {
Self {
lobbys: Mutex::new(HashMap::new()),
games: Mutex::new(HashMap::new()),
}
}
}

pub struct Protected<PLAYER: Player, ROOM: Room<PLAYER>>(Arc<Mutex<ROOM>>, PhantomData<PLAYER>);

impl<PLAYER: Player, ROOM: Room<PLAYER>> Protected<PLAYER, ROOM> {
pub fn new(content: ROOM) -> Self {
Self(Arc::new(Mutex::new(content)), PhantomData)
}

pub fn lock(&self) -> MutexGuard<ROOM> {
self.0.lock().unwrap()
}
}

impl<PLAYER: Player, ROOM: Room<PLAYER>> Clone for Protected<PLAYER, ROOM> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0), PhantomData)
}
}
188 changes: 129 additions & 59 deletions src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use rand::{
seq::{IteratorRandom, SliceRandom},
thread_rng,
};
use rocket::serde::Serialize;
use rocket::serde::{Deserialize, Serialize};
use std::{collections::HashMap, hash::Hash};

macro_rules! repeated_vec {
Expand All @@ -21,19 +21,116 @@ macro_rules! repeated_vec {
};
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
#[serde(rename_all = "lowercase")]
pub enum Team {
Sherlock,
Moriarty,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
#[serde(rename_all = "lowercase")]
pub enum Cable {
Safe,
Defusing,
Bomb,
}

pub trait Player {
type ID: Eq + Hash + Clone + Copy;

fn id(&self) -> Self::ID;
}

pub trait Room<PLAYER: Player> {
fn name(&self) -> &str;
fn players(&self) -> &HashMap<PLAYER::ID, PLAYER>;
fn get_player(&self, id: PLAYER::ID) -> Option<&PLAYER>;
fn get_player_mut(&mut self, id: PLAYER::ID) -> Option<&mut PLAYER>;
}

pub trait WaitingPlayer: Player {
fn ready(&self) -> bool;
}

pub struct Lobby<PLAYER: WaitingPlayer> {
name: String,
players: HashMap<PLAYER::ID, PLAYER>,
}

impl<PLAYER: WaitingPlayer> Lobby<PLAYER> {
pub fn new(name: String) -> Self {
Self {
name,
players: HashMap::new(),
}
}

pub fn add_player(&mut self, player: PLAYER) -> Result<(), errors::Join> {
if self.players.len() >= 8 {
return Err(errors::Join::GameFull);
}

if self.players.contains_key(&player.id()) {
return Err(errors::Join::AlreadyConnected);
}
self.players.insert(player.id(), player);

Ok(())
}

pub fn remove_player(&mut self, id: PLAYER::ID) {
self.players.remove(&id);
}
}

impl<PLAYER: WaitingPlayer> Room<PLAYER> for Lobby<PLAYER> {
fn name(&self) -> &str {
&self.name
}

fn players(&self) -> &HashMap<PLAYER::ID, PLAYER> {
&self.players
}

fn get_player(&self, id: PLAYER::ID) -> Option<&PLAYER> {
self.players.get(&id)
}

fn get_player_mut(&mut self, id: PLAYER::ID) -> Option<&mut PLAYER> {
self.players.get_mut(&id)
}
}

pub mod errors {
use thiserror::Error;

#[derive(Error, Debug, Clone, Copy)]
pub enum Join {
#[error("this game is already full")]
GameFull,
#[error("you are already connected to this game")]
AlreadyConnected,
}
}

//
//
//
//
//
//
//
//
//
//
//
//
//

pub trait OldPlayer: WaitingPlayer {
fn connected(&self) -> bool;

fn team(&self) -> Team;
Expand All @@ -44,22 +141,13 @@ pub trait Player {
fn cables(&self) -> &[Cable];
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(crate = "rocket::serde")]
#[serde(rename_all = "lowercase")]
pub enum Cable {
Safe,
Defusing,
Bomb,
}

pub enum CutOutcome {
Win(Team),
RoundEnd,
Nothing,
}

enum GameState<PLAYER: Player> {
enum GameState<PLAYER: OldPlayer> {
Lobby,
Ingame {
wire_cutters: PLAYER::ID,
Expand All @@ -68,57 +156,44 @@ enum GameState<PLAYER: Player> {
},
}

pub struct Game<PLAYER: Player> {
pub struct Game<PLAYER: OldPlayer> {
name: String,
players: HashMap<PLAYER::ID, PLAYER>,
state: GameState<PLAYER>,
}

impl<PLAYER: Player> Game<PLAYER> {
pub fn new(name: String) -> Self {
Self {
name,
players: HashMap::new(),
state: GameState::Lobby,
}
}

pub fn name(&self) -> &str {
impl<PLAYER: Player + OldPlayer> Room<PLAYER> for Game<PLAYER> {
fn name(&self) -> &str {
&self.name
}

pub const fn wire_cutters(&self) -> Option<PLAYER::ID> {
if let GameState::Ingame { wire_cutters, .. } = self.state {
Some(wire_cutters)
} else {
None
}
}

pub const fn players(&self) -> &HashMap<PLAYER::ID, PLAYER> {
fn players(&self) -> &HashMap<PLAYER::ID, PLAYER> {
&self.players
}

pub fn get_player(&self, id: PLAYER::ID) -> Option<&PLAYER> {
fn get_player(&self, id: PLAYER::ID) -> Option<&PLAYER> {
self.players.get(&id)
}

pub fn get_player_mut(&mut self, id: PLAYER::ID) -> Option<&mut PLAYER> {
fn get_player_mut(&mut self, id: PLAYER::ID) -> Option<&mut PLAYER> {
self.players.get_mut(&id)
}
}

pub fn add_player(&mut self, player: PLAYER) -> Result<(), errors::PlayerJoin> {
match self.state {
GameState::Lobby { .. } => {
if self.players.len() >= 8 {
return Err(errors::PlayerJoin::GameFull);
}

self.players.entry(player.id()).or_insert(player);
impl<PLAYER: OldPlayer> Game<PLAYER> {
pub fn new(name: String) -> Self {
Self {
name,
players: HashMap::new(),
state: GameState::Lobby,
}
}

Ok(())
}
_ => Err(errors::PlayerJoin::GameAlreadyStarted),
pub const fn wire_cutters(&self) -> Option<PLAYER::ID> {
if let GameState::Ingame { wire_cutters, .. } = self.state {
Some(wire_cutters)
} else {
None
}
}

Expand Down Expand Up @@ -147,12 +222,12 @@ impl<PLAYER: Player> Game<PLAYER> {
}
}

pub fn start(&mut self) -> Result<(), errors::GameStart> {
pub fn start(&mut self) -> Result<(), old_errors::GameStart> {
if self.players.len() < 4 {
return Err(errors::GameStart::NotEnoughPlayers);
return Err(old_errors::GameStart::NotEnoughPlayers);
}
if !self.players.values().all(Player::ready) {
return Err(errors::GameStart::NotAllPlayersReady);
if !self.players.values().all(WaitingPlayer::ready) {
return Err(old_errors::GameStart::NotAllPlayersReady);
}

let mut teams = match self.players.len() {
Expand Down Expand Up @@ -185,18 +260,18 @@ impl<PLAYER: Player> Game<PLAYER> {
&mut self,
cutting: PLAYER::ID,
cutted: PLAYER::ID,
) -> Result<(Cable, CutOutcome), errors::Cut> {
) -> Result<(Cable, CutOutcome), old_errors::Cut> {
if let GameState::Ingame {
wire_cutters,
defusing_remaining,
cutted_count,
} = &mut self.state
{
if cutting != *wire_cutters {
return Err(errors::Cut::DontHaveWireCutter);
return Err(old_errors::Cut::DontHaveWireCutter);
}
if cutted == cutting {
return Err(errors::Cut::CannotSelfCut);
return Err(old_errors::Cut::CannotSelfCut);
}

let cable = self.players.get_mut(&cutted).unwrap().cut_cable();
Expand All @@ -219,7 +294,7 @@ impl<PLAYER: Player> Game<PLAYER> {
Ok((cable, CutOutcome::Nothing))
}
} else {
Err(errors::Cut::GameNotStarted)
Err(old_errors::Cut::GameNotStarted)
}
}

Expand All @@ -244,12 +319,7 @@ impl<PLAYER: Player> Game<PLAYER> {
}
}

pub mod errors {
pub enum PlayerJoin {
GameFull,
GameAlreadyStarted,
}

pub mod old_errors {
pub enum GameStart {
NotEnoughPlayers,
NotAllPlayersReady,
Expand Down
Loading

0 comments on commit feed6fa

Please sign in to comment.