aboutsummaryrefslogtreecommitdiff
path: root/games/rstnode/rst-core/src/server.rs
//! Game server state handler
//!
//! A server can host many lobbies at the same time.  It listens for
//! connections according to a address given to the initialiser.

use crate::{
    data::Player,
    lobby::LobbyList,
    map::Map,
    users::UserStore,
    wire::{
        game::Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr,
        Response, UpdateState, User, UserId,
    },
    GameIf, Match,
};
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use std::{collections::BTreeMap, path::Path};

/// A convenience result wrapper for server actions
pub type ServerResult<T> = Result<T, ServerErr>;
pub enum ServerErr {
    /// The requested directory is corrupted
    NoSuchDir,
    /// Corrupted game state
    Corrupted,
    /// No such match found
    NoSuchMatch,
}

/// The game's server backend
pub struct Server {
    matches: BTreeMap<MatchId, Mutex<Match>>,
    users: UserStore,
    lobbies: LobbyList,
}

impl Server {
    /// Create a new game server
    pub(crate) fn new() -> Arc<Self> {
        Arc::new(Self {
            matches: Default::default(),
            users: UserStore::new(),
            lobbies: LobbyList::new(),
        })
    }

    /// Open the state dir of a game server
    pub async fn open(self: Arc<Self>, path: &Path) -> ServerResult<()> {
        Ok(())
    }

    /// Stop accepting new game connections and shutdown gracefully
    ///
    /// Returns the number of matches still going on.
    pub async fn shutdown(self: Arc<Self>) -> ServerResult<u64> {
        Ok(0)
    }

    /// Save and close the statedir and kicking all players
    ///
    /// Returns the number of players that were kicked off the server
    /// prematurely.
    pub async fn kill(self: Arc<Self>) -> ServerResult<u64> {
        Ok(0)
    }

    // pub async fn update_map<F>(self: Arc<Self>, id: MatchId, cb: F) -> ServerResult<Response>
    // where
    //     F: Fn(&mut Map) -> ServerResult<Response>,
    // {
    //     match self.matches.get(&id) {
    //         Some(ref m) => m.lock().await.map.update(cb),
    //         None => Err(ServerErr::NoSuchMatch),
    //     }
    // }

    pub async fn update_players<F>(self: Arc<Self>, id: MatchId, cb: F) -> ServerResult<Response>
    where
        F: Fn(&mut Vec<Player>) -> ServerResult<Response>,
    {
        match self.matches.get(&id) {
            Some(ref mut m) => cb(&mut m.lock().await.players),
            None => Err(ServerErr::NoSuchMatch),
        }
    }
}

#[async_trait]
impl GameIf for Server {
    async fn register(self: Arc<Self>, _name: String, _pw: String) -> Result<UserId, RegErr> {
        todo!()
    }

    async fn login(self: Arc<Self>, _name: String, _pw: String) -> Result<User, AuthErr> {
        todo!()
    }

    async fn logout(self: Arc<Self>, _user: User) -> Result<(), AuthErr> {
        todo!()
    }

    async fn anonymous(self: Arc<Self>, name: String) -> Result<User, AuthErr> {
        // let (_, auth) = self.users.add(name, None, true).await;
        // Ok(auth)
        todo!()
    }

    async fn join(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<Lobby, LobbyErr> {
        let mu = self.users.get(&user).await?;
        self.lobbies.get_mut(lobby, |l| l.join(&mu)).await
    }

    async fn leave(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<(), LobbyErr> {
        let mu = self.users.get(&user).await?;
        self.lobbies.get_mut(lobby, |l| l.leave(&mu)).await
    }

    async fn ready(
        self: Arc<Self>,
        user: User,
        lobby: LobbyId,
        ready: bool,
    ) -> Result<LobbyUpdate, LobbyErr> {
        self.lobbies.get_mut(lobby, |l| l.ready(user, ready)).await
    }

    /// A start request was received
    async fn start_req(
        self: Arc<Self>,
        user: UserId,
        lobby: LobbyId,
    ) -> Result<DateTime<Utc>, LobbyErr> {
        self.lobbies.get_mut(lobby, |l| l.start(user)).await??;
        let _lob = self.lobbies.consume(lobby).await?;
        Ok(Utc::now())
    }

    async fn perform_action(
        self: Arc<Self>,
        user: User,
        mtch: MatchId,
        act: Action,
    ) -> UpdateState {
        unimplemented!()
    }

    async fn leave_match(self: Arc<Self>, _user: User, _mtch: MatchId) -> Result<(), MatchErr> {
        unimplemented!()
    }
}