server/src/Server.hs

118 lines
3.7 KiB
Haskell
Raw Normal View History

2018-04-11 13:25:24 +02:00
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
2018-04-11 13:25:24 +02:00
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
2018-04-11 13:25:24 +02:00
module Server (
T(..)
, close
, get
2018-04-11 13:25:24 +02:00
, logIn
, new
, register
, room
, select
, sessionsWhere
, update
2018-04-11 13:25:24 +02:00
) where
2019-12-17 15:01:33 +01:00
import Data.Map (Map, (!), (!?), adjust, delete, insert, mapMaybe)
import qualified Data.Map as Map (empty, foldlWithKey, lookup)
import Data.Text (Text)
import Hanafuda.KoiKoi (PlayerID)
import Hanafuda.Message (Room)
import Keys (getKeys)
import qualified Keys (T)
import qualified Player (T(..))
2019-10-13 21:52:28 +02:00
import qualified RW (RW(..))
import qualified Session (ID, T(..), setPlayer)
import System.Random (Random(..))
2018-04-11 13:25:24 +02:00
2019-12-17 15:01:33 +01:00
type SessionIDs = Map PlayerID [Session.ID]
type Sessions = Map Session.ID Session.T
2018-04-11 13:25:24 +02:00
data T = T {
keys :: Keys.T
, sessionIDsByPlayerID :: SessionIDs
, sessions :: Sessions
2018-04-11 13:25:24 +02:00
}
instance RW.RW SessionIDs T where
get = sessionIDsByPlayerID
set sessionIDsByPlayerID server = server {sessionIDsByPlayerID}
2018-04-11 13:25:24 +02:00
2019-10-13 21:52:28 +02:00
instance RW.RW Sessions T where
get = sessions
set sessions server = server {sessions}
new :: IO T
new = getKeys >>= \keys -> return $ T {
keys
, sessionIDsByPlayerID = Map.empty
, sessions = Map.empty
2018-04-11 13:25:24 +02:00
}
room :: T -> Room
2019-12-17 15:01:33 +01:00
room = mapMaybe keepName . select (\_ -> Session.player)
where
keepName [] = Nothing
keepName (player:_) = Just $ Player.name player
2019-12-17 15:01:33 +01:00
push :: Ord k => k -> v -> Map k [v] -> Map k [v]
push key value m =
(maybe (insert key [value]) (insert key . (value:)) $ Map.lookup key m) m
select :: (PlayerID -> Session.T -> Maybe a) -> T -> Map PlayerID [a]
select selector (T {sessionIDsByPlayerID, sessions}) =
2019-12-17 15:01:33 +01:00
Map.foldlWithKey (\accumulator playerID sessionIDs ->
foldl (\acc ->
maybe acc (\v -> push playerID v acc) . selected playerID
) accumulator sessionIDs
) Map.empty sessionIDsByPlayerID
where
selected playerID sessionID =
Map.lookup sessionID sessions >>= selector playerID
2019-12-17 15:01:33 +01:00
sessionsWhere :: (PlayerID -> Session.T -> Bool) -> T -> Map PlayerID [Session.T]
sessionsWhere predicate = select selectorOfPredicate where
selectorOfPredicate playerID session =
if predicate playerID session then Just session else Nothing
register :: forall a b. (Random a, Ord a, RW.RW (Map a b) T) => b -> T -> IO (T, a)
register x server = do
newID <- randomIO
return (RW.update (insert newID x) server, newID)
2019-10-13 21:52:28 +02:00
get :: forall a b. (Ord a, RW.RW (Map a b) T) => a -> T -> b
get keyID server = (RW.get server :: Map a b) ! keyID
2018-04-11 13:25:24 +02:00
2019-10-13 21:52:28 +02:00
update :: forall a b. (Ord a, RW.RW (Map a b) T) => a -> (b -> b) -> T -> T
update keyID updator =
RW.update (adjust updator keyID :: Map a b -> Map a b)
logIn :: Text -> PlayerID -> Session.ID -> T -> T
logIn name playerID sessionID =
2019-12-17 15:01:33 +01:00
RW.update (push playerID sessionID) .
update sessionID (Session.setPlayer playerID name)
close :: Monad m => Session.ID -> T -> m (T, Maybe PlayerID)
close sessionID server =
return . performUpdates $ popSession sessionID server
where
performUpdates (updateSessionIDs, mPlayerID) = (
RW.update (delete sessionID :: Sessions -> Sessions)
. RW.update (updateSessionIDs :: SessionIDs -> SessionIDs) $ server
, mPlayerID
)
popSession :: Session.ID -> T -> (SessionIDs -> SessionIDs, Maybe PlayerID)
popSession sessionID (T {sessions, sessionIDsByPlayerID}) =
case findPlayerID of
Nothing -> (id, Nothing)
Just (playerID, [_]) -> (delete playerID, Just playerID)
Just (playerID, _) -> (purgeSession playerID, Nothing)
where
findPlayerID = do
playerID <- fmap Player.playerID . Session.player =<< (sessions !? sessionID)
(,) playerID <$> (sessionIDsByPlayerID !? playerID)
purgeSession = adjust (filter (/= sessionID))