351 lines
12 KiB
Rust
351 lines
12 KiB
Rust
use core::{
|
|
num::NonZeroU8,
|
|
ops::{Deref, DerefMut},
|
|
time::Duration,
|
|
};
|
|
use std::collections::HashMap;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use tokio::sync::broadcast::Sender;
|
|
use werewolves_proto::{
|
|
error::GameError,
|
|
game::{Game, GameSettings, Village},
|
|
message::{
|
|
CharacterState, ClientMessage, DayCharacter, Identification, PlayerState, ServerMessage,
|
|
UpdateSelf,
|
|
host::{HostLobbyMessage, HostMessage, ServerToHostMessage},
|
|
},
|
|
player::PlayerId,
|
|
};
|
|
|
|
use crate::{
|
|
LogError,
|
|
communication::lobby::LobbyComms,
|
|
connection::JoinedPlayers,
|
|
game::GameRunner,
|
|
runner::{IdentifiedClientMessage, Message},
|
|
};
|
|
|
|
pub struct Lobby {
|
|
players_in_lobby: LobbyPlayers,
|
|
settings: GameSettings,
|
|
joined_players: JoinedPlayers,
|
|
comms: Option<LobbyComms>,
|
|
}
|
|
|
|
impl Lobby {
|
|
pub fn new(joined_players: JoinedPlayers, comms: LobbyComms) -> Self {
|
|
Self {
|
|
joined_players,
|
|
comms: Some(comms),
|
|
settings: GameSettings::default(),
|
|
players_in_lobby: LobbyPlayers(Vec::new()),
|
|
}
|
|
}
|
|
|
|
pub fn set_players_in_lobby(&mut self, players_in_lobby: LobbyPlayers) {
|
|
self.players_in_lobby = players_in_lobby
|
|
}
|
|
|
|
const fn comms(&mut self) -> Result<&mut LobbyComms, GameError> {
|
|
match self.comms.as_mut() {
|
|
Some(comms) => Ok(comms),
|
|
None => Err(GameError::InactiveGameObject),
|
|
}
|
|
}
|
|
|
|
pub async fn send_lobby_info_to_clients(&mut self) {
|
|
let players = self
|
|
.players_in_lobby
|
|
.iter()
|
|
.map(|(id, _)| id.public.clone())
|
|
.collect::<Box<[_]>>();
|
|
self.joined_players
|
|
.send_all_lobby(
|
|
players,
|
|
&self
|
|
.players_in_lobby
|
|
.iter()
|
|
.map(|(id, _)| id.player_id.clone())
|
|
.collect::<Box<[_]>>(),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
async fn get_lobby_player_list(&self) -> Box<[PlayerState]> {
|
|
let mut players = Vec::new();
|
|
for (player, _) in self.players_in_lobby.iter() {
|
|
players.push(PlayerState {
|
|
identification: player.clone(),
|
|
connected: self.joined_players.is_connected(&player.player_id).await,
|
|
});
|
|
}
|
|
|
|
players.into_boxed_slice()
|
|
}
|
|
|
|
pub async fn send_lobby_info_to_host(&mut self) -> Result<(), GameError> {
|
|
let players = self.get_lobby_player_list().await;
|
|
self.comms()?
|
|
.host()
|
|
.send(ServerToHostMessage::Lobby(players))
|
|
.map_err(|err| GameError::GenericError(err.to_string()))
|
|
}
|
|
|
|
pub async fn next(&mut self) -> Option<GameRunner> {
|
|
let msg = self
|
|
.comms()
|
|
.unwrap()
|
|
.next_message()
|
|
.await
|
|
.expect("get next message");
|
|
|
|
match self.next_inner(msg.clone()).await.map_err(|err| (msg, err)) {
|
|
Ok(None) => {}
|
|
Ok(Some(mut game)) => {
|
|
game.role_reveal().await;
|
|
match game.host_message(HostMessage::GetState) {
|
|
Ok(msg) => {
|
|
log::info!("initial message after night reveal: {msg:?}");
|
|
game.comms().host().send(msg).log_warn();
|
|
}
|
|
Err(err) => {
|
|
log::error!("processing get_state after role reveal to host: {err}")
|
|
}
|
|
}
|
|
|
|
return Some(game);
|
|
}
|
|
Err((Message::Host(_), err)) => self
|
|
.comms()
|
|
.unwrap()
|
|
.host()
|
|
.send(ServerToHostMessage::Error(err))
|
|
.log_warn(),
|
|
Err((
|
|
Message::Client(IdentifiedClientMessage {
|
|
identity:
|
|
Identification {
|
|
player_id,
|
|
public: _,
|
|
},
|
|
message: _,
|
|
}),
|
|
GameError::InvalidMessageForGameState,
|
|
)) => {
|
|
let _ = self
|
|
.players_in_lobby
|
|
.send_if_present(&player_id, ServerMessage::InvalidMessageForGameState);
|
|
}
|
|
Err((
|
|
Message::Client(IdentifiedClientMessage {
|
|
identity: Identification { player_id, public },
|
|
message: _,
|
|
}),
|
|
err,
|
|
)) => {
|
|
log::error!("processing message from {public} [{player_id}]: {err}");
|
|
let _ = self
|
|
.players_in_lobby
|
|
.send_if_present(&player_id, ServerMessage::Reset);
|
|
}
|
|
Err((Message::Connect(_), _)) | Err((Message::Disconnect(_), _)) => {}
|
|
}
|
|
None
|
|
}
|
|
|
|
async fn next_inner(&mut self, msg: Message) -> Result<Option<GameRunner>, GameError> {
|
|
match msg {
|
|
Message::Host(HostMessage::InGame(_))
|
|
| Message::Host(HostMessage::ForceRoleAckFor(_)) => {
|
|
return Err(GameError::InvalidMessageForGameState);
|
|
}
|
|
Message::Host(HostMessage::NewLobby) => self
|
|
.comms()
|
|
.unwrap()
|
|
.host()
|
|
.send(ServerToHostMessage::Error(
|
|
GameError::InvalidMessageForGameState,
|
|
))
|
|
.log_warn(),
|
|
Message::Host(HostMessage::Lobby(HostLobbyMessage::GetState))
|
|
| Message::Host(HostMessage::GetState) => self.send_lobby_info_to_host().await?,
|
|
Message::Host(HostMessage::Lobby(HostLobbyMessage::GetGameSettings)) => {
|
|
let msg = ServerToHostMessage::GameSettings(self.settings.clone());
|
|
let _ = self.comms().unwrap().host().send(msg);
|
|
}
|
|
Message::Host(HostMessage::Lobby(HostLobbyMessage::SetGameSettings(settings))) => {
|
|
settings.check()?;
|
|
self.settings = settings;
|
|
}
|
|
Message::Host(HostMessage::Lobby(HostLobbyMessage::Start)) => {
|
|
if self.players_in_lobby.len() < self.settings.min_players_needed() {
|
|
return Err(GameError::TooFewPlayers {
|
|
got: self.players_in_lobby.len() as _,
|
|
need: self.settings.min_players_needed() as _,
|
|
});
|
|
}
|
|
let playing_players = self
|
|
.players_in_lobby
|
|
.iter()
|
|
.map(|(id, _)| id.clone())
|
|
.collect::<Box<[_]>>();
|
|
let release_token = self
|
|
.joined_players
|
|
.start_game_with(
|
|
&playing_players
|
|
.iter()
|
|
.map(|id| id.player_id.clone())
|
|
.collect::<Box<[_]>>(),
|
|
)
|
|
.await?;
|
|
|
|
let game = Game::new(&playing_players, self.settings.clone())?;
|
|
assert_eq!(game.village().characters().len(), playing_players.len());
|
|
|
|
let (comms, recv) = self.comms.take().unwrap().into_inner();
|
|
return Ok(Some(GameRunner::new(
|
|
game,
|
|
comms,
|
|
self.players_in_lobby.clone(),
|
|
recv,
|
|
self.joined_players.clone(),
|
|
release_token,
|
|
)));
|
|
}
|
|
Message::Client(IdentifiedClientMessage {
|
|
identity,
|
|
message: ClientMessage::Hello,
|
|
}) => {
|
|
if self
|
|
.players_in_lobby
|
|
.iter_mut()
|
|
.any(|p| p.0.player_id == identity.player_id)
|
|
{
|
|
// Already have the player
|
|
return Ok(None);
|
|
}
|
|
if let Some(sender) = self.joined_players.get_sender(&identity.player_id).await {
|
|
self.players_in_lobby.push((identity, sender.clone()));
|
|
self.send_lobby_info_to_clients().await;
|
|
self.send_lobby_info_to_host().await?;
|
|
}
|
|
}
|
|
Message::Host(HostMessage::Lobby(HostLobbyMessage::Kick(player_id)))
|
|
| Message::Client(IdentifiedClientMessage {
|
|
identity:
|
|
Identification {
|
|
player_id,
|
|
public: _,
|
|
},
|
|
message: ClientMessage::Goodbye,
|
|
}) => {
|
|
log::error!("we are in there");
|
|
if let Some(remove_idx) = self
|
|
.players_in_lobby
|
|
.iter()
|
|
.enumerate()
|
|
.find_map(|(idx, p)| (p.0.player_id == player_id).then_some(idx))
|
|
{
|
|
log::error!("removing player {player_id} at idx {remove_idx}");
|
|
self.players_in_lobby.swap_remove(remove_idx);
|
|
self.send_lobby_info_to_host().await?;
|
|
self.send_lobby_info_to_clients().await;
|
|
}
|
|
}
|
|
Message::Client(IdentifiedClientMessage {
|
|
identity:
|
|
Identification {
|
|
player_id,
|
|
public: _,
|
|
},
|
|
message: ClientMessage::GetState,
|
|
}) => {
|
|
let msg = ServerMessage::LobbyInfo {
|
|
joined: self
|
|
.players_in_lobby
|
|
.iter()
|
|
.any(|(p, _)| p.player_id == player_id),
|
|
players: self
|
|
.players_in_lobby
|
|
.iter()
|
|
.map(|(id, _)| id.public.clone())
|
|
.collect(),
|
|
};
|
|
if let Some(sender) = self.joined_players.get_sender(&player_id).await {
|
|
sender.send(msg).log_debug();
|
|
}
|
|
}
|
|
Message::Client(IdentifiedClientMessage {
|
|
identity: _,
|
|
message: ClientMessage::RoleAck,
|
|
}) => return Err(GameError::InvalidMessageForGameState),
|
|
Message::Client(IdentifiedClientMessage {
|
|
identity: Identification { player_id, public },
|
|
message: ClientMessage::UpdateSelf(_),
|
|
}) => {
|
|
self.joined_players
|
|
.update(&player_id, move |p| {
|
|
p.name = public.name;
|
|
p.number = public.number;
|
|
p.pronouns = public.pronouns;
|
|
})
|
|
.await;
|
|
self.send_lobby_info_to_clients().await;
|
|
self.send_lobby_info_to_host().await.log_debug();
|
|
}
|
|
Message::Connect(_) | Message::Disconnect(_) => self.send_lobby_info_to_host().await?,
|
|
Message::Host(HostMessage::Echo(msg)) => {
|
|
self.comms()?.host().send(msg).log_warn();
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct LobbyPlayers(Vec<(Identification, Sender<ServerMessage>)>);
|
|
|
|
impl Deref for LobbyPlayers {
|
|
type Target = Vec<(Identification, Sender<ServerMessage>)>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl DerefMut for LobbyPlayers {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
impl LobbyPlayers {
|
|
pub fn find(&self, player_id: &PlayerId) -> Option<&Sender<ServerMessage>> {
|
|
self.iter()
|
|
.find_map(|(id, s)| (&id.player_id == player_id).then_some(s))
|
|
}
|
|
|
|
pub fn send_if_present(
|
|
&self,
|
|
player_id: &PlayerId,
|
|
message: ServerMessage,
|
|
) -> Result<(), GameError> {
|
|
if let Some(sender) = self.find(player_id) {
|
|
sender
|
|
.send(message)
|
|
.map(|_| ())
|
|
.map_err(|err| GameError::GenericError(err.to_string()))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub fn drain(&mut self) -> Self {
|
|
let mut swapped = Self(vec![]);
|
|
core::mem::swap(&mut swapped, self);
|
|
swapped
|
|
}
|
|
}
|