zero-trust-rps 0.0.4

Online Multiplayer Rock Paper Scissors
Documentation
use std::fmt::Display;
use std::hash::Hash;

use crate::common::connection::{WriteMessageError, Writer};
use crate::common::message::ServerMessage;
use crate::common::message::{RoomId, RoomState};

use super::types::{Connections, RoomData, Rooms};

pub async fn get_room_state<P: Hash>(rooms: &Rooms<P>, room_id: RoomId) -> Option<RoomState> {
    let rooms = rooms.lock().await;

    let room: &RoomData<_> = rooms.get(&room_id)?;

    let mut users: Box<[_]> = room.users.values().copied().collect();
    let round = room.round.clone();

    drop(rooms);

    users.sort_by_key(|u| u.id);

    Some(RoomState {
        id: room_id,
        users,
        round,
    })
}

#[derive(thiserror::Error, Debug)]
pub enum SendRoomUpdateError {
    #[error("{}", .0)]
    WriteMessageError(#[from] WriteMessageError),
    #[error("{}", .0)]
    Other(String),
}
impl From<String> for SendRoomUpdateError {
    fn from(value: String) -> Self {
        SendRoomUpdateError::Other(value)
    }
}

pub async fn send_room_update<P: Display + std::hash::Hash + Clone + Eq, W: Writer>(
    room_id: RoomId,
    rooms: &Rooms<P>,
    streams: &Connections<P, W>,
) -> Result<(), SendRoomUpdateError> {
    let new_state = get_room_state(rooms, room_id)
        .await
        .ok_or_else(|| format!("No state for {room_id:?}"))?;

    if let Some(set) = rooms.lock().await.get(&room_id) {
        for (peer, user) in set.users.iter() {
            let mut guard = streams.lock().await;
            let stream = if let Some(stream) = guard.get_mut(peer) {
                stream
            } else {
                continue;
            };

            log::info!(
                "Calling room_update for {peer} ({}) in room {room_id}",
                user.id
            );
            stream
                .write_message(ServerMessage::RoomUpdate {
                    new_state: new_state.clone(),
                })
                .await?;
        }
    }

    Ok(())
}