// Copyright (C) 2025-2026 Emilis Bliūdžius
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
use core::{
num::NonZeroU8,
ops::{Deref, DerefMut},
};
use tokio::sync::broadcast::Sender;
use werewolves_proto::{
error::GameError,
game::{Game, GameSettings},
message::{
ClientMessage, Identification, PlayerState, PublicIdentity, ServerMessage,
host::{HostLobbyMessage, HostMessage, ServerToHostMessage},
},
player::PlayerId,
};
use crate::{
LogError,
communication::lobby::LobbyComms,
connection::JoinedPlayers,
game::GameRunner,
runner::{ClientUpdate, IdentifiedClientMessage, Message},
};
pub struct Lobby {
players_in_lobby: LobbyPlayers,
settings: GameSettings,
joined_players: JoinedPlayers,
comms: Option,
qr_mode: bool,
}
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()),
qr_mode: false,
}
}
pub fn set_settings(&mut self, settings: GameSettings) {
self.settings = settings.clone();
}
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::>();
self.joined_players
.send_all_lobby(
players,
&self
.players_in_lobby
.iter()
.map(|(id, _)| id.player_id)
.collect::>(),
)
.await;
}
async fn get_lobby_info(&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_info().await;
let qr_mode = self.qr_mode;
let settings = self.settings.clone();
let host = self.comms()?.host();
host.send(ServerToHostMessage::Lobby { players, settings })?;
host.send(ServerToHostMessage::QrMode(qr_mode))
}
pub async fn next(&mut self) -> Option {
let msg = match self.comms().unwrap().next_message().await {
Ok(msg) => msg,
Err(err) => {
log::error!("get next message: {err}");
return None;
}
};
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, .. },
..
}),
GameError::InvalidMessageForGameState,
)) => {
let _ = self
.players_in_lobby
.send_if_present(player_id, ServerMessage::InvalidMessageForGameState);
}
Err((
Message::Client(IdentifiedClientMessage {
update: ClientUpdate::ConnectStateUpdate,
..
}),
_,
)) => {}
Err((
Message::Client(IdentifiedClientMessage {
identity: Identification { player_id, public },
..
}),
err,
)) => {
log::error!("processing message from {public} [{player_id}]: {err}");
let _ = self
.players_in_lobby
.send_if_present(player_id, ServerMessage::Reset);
}
}
None
}
async fn next_inner(&mut self, msg: Message) -> Result