Follow renaming (Key -> ID) in hanafuda lib, finish taking IDs declaration out of the webapp and simplify Automaton by putting some operations back into Game module

This commit is contained in:
Tissevert 2019-08-24 22:17:52 +02:00
parent 539b74990e
commit 3e7c0a88f1
8 changed files with 110 additions and 153 deletions

View file

@ -16,13 +16,13 @@ import Data.Map ((!))
import Control.Concurrent (MVar, modifyMVar, putMVar, readMVar, takeMVar) import Control.Concurrent (MVar, modifyMVar, putMVar, readMVar, takeMVar)
import Control.Monad.Reader (ReaderT(..), ask, asks, lift) import Control.Monad.Reader (ReaderT(..), ask, asks, lift)
import Network.WebSockets (Connection) import Network.WebSockets (Connection)
import Hanafuda.KoiKoi (PlayerKey) import Hanafuda.KoiKoi (PlayerID)
import qualified Session (T(..)) import qualified Session (T(..))
import qualified Server (T(..)) import qualified Server (T(..))
data Context = Context { data Context = Context {
mServer :: MVar Server.T mServer :: MVar Server.T
, key :: PlayerKey , playerID :: PlayerID
} }
type T a = ReaderT Context IO a type T a = ReaderT Context IO a
@ -30,20 +30,20 @@ type T a = ReaderT Context IO a
server :: T Server.T server :: T Server.T
server = asks mServer >>= lift . readMVar server = asks mServer >>= lift . readMVar
get :: PlayerKey -> T Session.T get :: PlayerID -> T Session.T
get key = get playerID =
(! key) . Server.sessions <$> server (! playerID) . Server.sessions <$> server
current :: T Session.T current :: T Session.T
current = do current = do
asks key >>= get asks playerID >>= get
connection :: T Connection connection :: T Connection
connection = Session.connection <$> current connection = Session.connection <$> current
debug :: String -> T () debug :: String -> T ()
debug message = debug message =
show <$> asks key show <$> asks playerID
>>= lift . putStrLn . (++ ' ':message) >>= lift . putStrLn . (++ ' ':message)
try :: (Server.T -> Either String Server.T) -> T (Maybe String) try :: (Server.T -> Either String Server.T) -> T (Maybe String)

View file

@ -3,83 +3,85 @@ module Automaton (
start start
) where ) where
import Control.Monad.Except (runExceptT) import Control.Monad.Reader (asks)
import Control.Monad.Reader (asks, lift)
import Control.Monad.Writer (runWriterT)
import qualified Data (RW(..)) import qualified Data (RW(..))
import Data.Map (Map, (!?)) import Data.Map (Map, (!?))
import qualified Game (Key, T, new, play) import qualified Game (new, play)
import qualified Hanafuda.KoiKoi as KoiKoi (GameBlueprint(..), Step(..)) import qualified Hanafuda.KoiKoi as KoiKoi (
Game, GameBlueprint(..), GameID, Step(..)
)
import qualified Session (Status(..), T(..), Update) import qualified Session (Status(..), T(..), Update)
import qualified Server (endGame, get, logIn, logOut, update, register, room) import qualified Server (endGame, get, logIn, logOut, update, room)
import qualified App (Context(..), T, current, debug, get, server, try, update, update_) import qualified App (Context(..), T, current, debug, get, server, try, update_)
import qualified Hanafuda.Message as Message (FromClient(..), T(..)) import qualified Hanafuda.Message as Message (FromClient(..), T(..))
import qualified Messaging (broadcast, get, notifyPlayers, relay, send, sendTo, update) import qualified Messaging (
broadcast, get, notifyPlayers, relay, send, sendTo, update
)
receive :: Session.Status -> Message.FromClient -> App.T () receive :: Session.Status -> Message.FromClient -> App.T ()
receive (Session.LoggedIn False) logIn@(Message.LogIn login) = receive (Session.LoggedIn False) logIn@(Message.LogIn login) =
asks App.key >>= App.try . (Server.logIn login) asks App.playerID >>= App.try . (Server.logIn login)
>>= maybe >>= maybe
(Messaging.relay logIn Messaging.broadcast >> setSessionStatus (Session.LoggedIn True)) (Messaging.relay logIn Messaging.broadcast >> setSessionStatus (Session.LoggedIn True))
sendError sendError
receive (Session.LoggedIn True) logOut@Message.LogOut = do receive (Session.LoggedIn True) logOut@Message.LogOut = do
Messaging.relay logOut Messaging.broadcast Messaging.relay logOut Messaging.broadcast
asks App.key >>= App.update_ . Server.logOut asks App.playerID >>= App.update_ . Server.logOut
setSessionStatus (Session.LoggedIn False) setSessionStatus (Session.LoggedIn False)
receive (Session.LoggedIn True) invitation@(Message.Invitation {Message.to}) = do receive (Session.LoggedIn True) invitation@(Message.Invitation {Message.to}) = do
session <- App.get to session <- App.get to
case Session.status session of case Session.status session of
Session.LoggedIn True -> do Session.LoggedIn True -> do
key <- asks App.key from <- asks App.playerID
App.update_ (Server.update to (Data.set $ Session.Answering key :: Session.Update)) App.update_ (Server.update to (Data.set $ Session.Answering from :: Session.Update))
Messaging.broadcast $ Messaging.update {Message.paired = [key, to]} Messaging.broadcast $ Messaging.update {Message.paired = [from, to]}
(Messaging.relay invitation $ Messaging.sendTo [to]) (Messaging.relay invitation $ Messaging.sendTo [to])
setSessionStatus (Session.Waiting to) setSessionStatus (Session.Waiting to)
_ -> sendError "They just left" _ -> sendError "They just left"
receive (Session.Answering to) message@(Message.Answer {Message.accept}) = do receive (Session.Answering to) message@(Message.Answer {Message.accept}) = do
session <- App.get to session <- App.get to
key <- asks App.key playerID <- asks App.playerID
case Session.status session of case Session.status session of
Session.Waiting for | for == key -> do Session.Waiting for | for == playerID -> do
Messaging.relay message $ Messaging.sendTo [to] Messaging.relay message $ Messaging.sendTo [to]
newStatus <- newStatus <-
if accept if accept
then do then do
gameKey <- Server.register <$> (lift $ Game.new for to) >>= App.update gameID <- Game.new (for, to)
game <- Server.get gameKey <$> App.server game <- Server.get gameID <$> App.server
Messaging.notifyPlayers game [] Messaging.notifyPlayers game []
return $ Session.Playing gameKey return $ Session.Playing gameID
else do else do
Messaging.broadcast $ Messaging.update {Message.alone = [key, to]} Messaging.broadcast $ Messaging.update {Message.alone = [for, to]}
return $ Session.LoggedIn True return $ Session.LoggedIn True
App.update_ $ Server.update to (Data.set newStatus :: Session.Update) App.update_ $ Server.update to (Data.set newStatus :: Session.Update)
setSessionStatus newStatus setSessionStatus newStatus
_ -> sendError "They're not waiting for your answer" _ -> sendError "They're not waiting for your answer"
receive (Session.Playing gameKey) played@(Message.Play {}) = do receive (Session.Playing gameID) played@(Message.Play {}) = do
key <- asks App.key playerID <- asks App.playerID
game <- Server.get gameKey <$> App.server game <- Server.get gameID <$> App.server
(result, logs) <- lift . runWriterT . runExceptT $ Game.play key (Message.move played) game (result, logs) <- Game.play playerID (Message.move played) game
case result of case result of
Left message -> sendError message Left message -> sendError message
Right newGame -> do Right newGame -> do
case KoiKoi.step newGame of case KoiKoi.step newGame of
KoiKoi.Over -> do KoiKoi.Over -> do
App.debug $ "Game " ++ show gameKey ++ " ended" App.debug $ "Game " ++ show gameID ++ " ended"
App.update_ $ Server.endGame gameKey App.update_ $ Server.endGame gameID
_ -> App.update_ $ Server.update gameKey (const newGame) _ -> App.update_ $ Server.update gameID (const newGame)
Messaging.notifyPlayers newGame logs Messaging.notifyPlayers newGame logs
receive (Session.Playing gameKey) Message.Quit = do receive (Session.Playing gameID) Message.Quit = do
games <- (Data.get <$> App.server :: App.T (Map Game.Key Game.T)) games <- (Data.get <$> App.server :: App.T (Map KoiKoi.GameID KoiKoi.Game))
case games !? gameKey of case games !? gameID of
Nothing -> do Nothing -> do
key <- asks App.key playerID <- asks App.playerID
Messaging.broadcast $ Messaging.update {Message.alone = [key]} Messaging.broadcast $ Messaging.update {Message.alone = [playerID]}
setSessionStatus (Session.LoggedIn True) setSessionStatus (Session.LoggedIn True)
_ -> sendError "Game is still running" _ -> sendError "Game is still running"
@ -90,8 +92,8 @@ sendError = Messaging.send . Message.Error
setSessionStatus :: Session.Status -> App.T () setSessionStatus :: Session.Status -> App.T ()
setSessionStatus newStatus = do setSessionStatus newStatus = do
key <- asks App.key playerID <- asks App.playerID
App.update_ $ Server.update key $ (Data.set newStatus :: Session.Update) App.update_ $ Server.update playerID $ (Data.set newStatus :: Session.Update)
App.debug $ show newStatus App.debug $ show newStatus
loop :: App.T () loop :: App.T ()
@ -104,5 +106,5 @@ loop = do
start :: App.T () start :: App.T ()
start = do start = do
App.debug "Initial state" App.debug "Initial state"
Message.Welcome . Server.room <$> App.server <*> asks App.key >>= Messaging.send Message.Welcome . Server.room <$> App.server <*> asks App.playerID >>= Messaging.send
loop loop

View file

@ -1,30 +1,11 @@
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DeriveGeneric #-}
module Data ( module Data (
Key(..) RW(..)
, RW(..)
) where ) where
import Data.Aeson (FromJSON(..), ToJSON(..), ToJSONKey(..), genericToEncoding)
import Data.Aeson.Types (toJSONKeyText)
import Data.Text (pack)
import GHC.Generics
import qualified JSON (defaultOptions)
class RW a b where class RW a b where
get :: b -> a get :: b -> a
set :: a -> b -> b set :: a -> b -> b
update :: (a -> a) -> b -> b update :: (a -> a) -> b -> b
update f v = update f v =
set (f (get v)) v set (f (get v)) v
newtype Key a = Key Int deriving (Eq, Ord, Enum, Read, Show, Generic)
instance FromJSON (Key a)
instance ToJSON (Key a) where
toEncoding = genericToEncoding JSON.defaultOptions
instance ToJSONKey (Key a) where
toJSONKey = toJSONKeyText (pack . \(Key n) -> show n)

View file

@ -1,49 +1,40 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
module Game ( module Game (
Key export
, T
, export
, new , new
, play , play
) where ) where
import Control.Monad.Except (throwError) import qualified App (T, update)
import Control.Monad.Except (runExceptT, throwError)
import Control.Monad.Reader (lift)
import Control.Monad.Writer (runWriterT)
import Data.Map (mapWithKey) import Data.Map (mapWithKey)
import qualified Data (Key)
import qualified Hanafuda (empty) import qualified Hanafuda (empty)
import qualified Hanafuda.KoiKoi (Game, Environment, Mode(..), Move(..), PlayerKey, new, play) import qualified Hanafuda.KoiKoi as KoiKoi (
import Hanafuda.KoiKoi (GameBlueprint(..)) Action, Move(..), play, new
)
import Hanafuda.KoiKoi (Game, GameBlueprint(..), GameID, Mode(..), PlayerID)
import qualified Hanafuda.Player (Player(..), Players(..)) import qualified Hanafuda.Player (Player(..), Players(..))
import Hanafuda.Message (PublicGame) import Hanafuda.Message (PublicGame)
import qualified Server (register)
new :: (PlayerID, PlayerID) -> App.T GameID
new (for, to) =
Server.register <$> (lift $ KoiKoi.new (for, to) WholeYear) >>= App.update
type T = Hanafuda.KoiKoi.Game export :: PlayerID -> Game -> PublicGame
export playerID game = game {
type Key = Data.Key T
new :: Hanafuda.KoiKoi.PlayerKey -> Hanafuda.KoiKoi.PlayerKey -> IO T
new p1 p2 = do
Hanafuda.KoiKoi.new [p1, p2] $ Hanafuda.KoiKoi.WholeYear
export :: Hanafuda.KoiKoi.PlayerKey -> T -> PublicGame
export key game = game {
deck = length $ deck game deck = length $ deck game
, players = Hanafuda.Player.Players $ mapWithKey maskOpponentsHand unfiltered , players = Hanafuda.Player.Players $ mapWithKey maskOpponentsHand unfiltered
} }
where where
Hanafuda.Player.Players unfiltered = Hanafuda.KoiKoi.players game Hanafuda.Player.Players unfiltered = Hanafuda.KoiKoi.players game
maskOpponentsHand k player maskOpponentsHand k player
| k == key = player | k == playerID = player
| otherwise = player {Hanafuda.Player.hand = Hanafuda.empty} | otherwise = player {Hanafuda.Player.hand = Hanafuda.empty}
play :: Hanafuda.KoiKoi.Environment m => Hanafuda.KoiKoi.PlayerKey -> Hanafuda.KoiKoi.Move -> T -> m T play :: PlayerID -> KoiKoi.Move -> Game -> App.T (Either String Game, [KoiKoi.Action])
play key move game play playerID move game = lift . runWriterT . runExceptT $
| Hanafuda.KoiKoi.playing game == key = if playing game == playerID
Hanafuda.KoiKoi.play move game then KoiKoi.play move game
| otherwise = throwError "Not your turn" else throwError "Not your turn"

View file

@ -20,7 +20,7 @@ import qualified Automaton (start)
exit :: App.T () exit :: App.T ()
exit = do exit = do
asks App.key >>= App.update_ . Server.disconnect asks App.playerID >>= App.update_ . Server.disconnect
relay Message.LogOut broadcast relay Message.LogOut broadcast
serverApp :: App.T () -> App.T () -> IO ServerApp serverApp :: App.T () -> App.T () -> IO ServerApp
@ -28,8 +28,8 @@ serverApp onEnter onExit = do
mServer <- newMVar Server.new mServer <- newMVar Server.new
return $ \pending -> do return $ \pending -> do
session <- Session.open <$> acceptRequest pending session <- Session.open <$> acceptRequest pending
key <- modifyMVar mServer (return . Server.register session) playerID <- modifyMVar mServer (return . Server.register session)
let app = App.Context {App.mServer, App.key} let app = App.Context {App.mServer, App.playerID}
finally finally
(runReaderT onEnter app) (runReaderT onEnter app)
(runReaderT onExit app) (runReaderT onExit app)

View file

@ -20,28 +20,28 @@ import Data.Aeson (eitherDecode', encode)
import Network.WebSockets (receiveData, sendTextData) import Network.WebSockets (receiveData, sendTextData)
import Data.ByteString.Lazy.Char8 (unpack) import Data.ByteString.Lazy.Char8 (unpack)
import Control.Monad.Reader (asks, lift) import Control.Monad.Reader (asks, lift)
import qualified Game (T, export) import qualified Game (export)
import qualified Session (T(..)) import qualified Session (T(..))
import qualified Server (T(..), get) import qualified Server (T(..), get)
import qualified App (Context(..), T, connection, debug, server) import qualified App (Context(..), T, connection, debug, server)
import qualified Hanafuda.KoiKoi as KoiKoi (Action, GameBlueprint(..), PlayerKey) import qualified Hanafuda.KoiKoi as KoiKoi (Action, Game, GameBlueprint(..), PlayerID)
import qualified Hanafuda.Message as Message (T) import qualified Hanafuda.Message as Message (T)
import Hanafuda.Message (FromClient(..), T(..)) import Hanafuda.Message (FromClient(..), T(..))
sendTo :: [KoiKoi.PlayerKey] -> Message.T -> App.T () sendTo :: [KoiKoi.PlayerID] -> Message.T -> App.T ()
sendTo playerKeys obj = do sendTo playerIDs obj = do
sessions <- getSessions <$> App.server sessions <- getSessions <$> App.server
App.debug $ '(' : intercalate ", " recipients ++ ") <" ++ (unpack encoded) App.debug $ '(' : intercalate ", " recipients ++ ") <" ++ (unpack encoded)
lift $ forM_ (Session.connection <$> sessions) $ flip sendTextData encoded lift $ forM_ (Session.connection <$> sessions) $ flip sendTextData encoded
where where
encoded = encode $ obj encoded = encode $ obj
getSessions server = (\key -> Server.get key server) <$> playerKeys getSessions server = (\playerID -> Server.get playerID server) <$> playerIDs
recipients = show <$> playerKeys recipients = show <$> playerIDs
send :: Message.T -> App.T () send :: Message.T -> App.T ()
send obj = do send obj = do
key <- asks App.key playerID <- asks App.playerID
sendTo [key] obj sendTo [playerID] obj
broadcast :: Message.T -> App.T () broadcast :: Message.T -> App.T ()
broadcast obj = broadcast obj =
@ -50,7 +50,7 @@ broadcast obj =
relay :: FromClient -> (Message.T -> App.T ()) -> App.T () relay :: FromClient -> (Message.T -> App.T ()) -> App.T ()
relay message f = do relay message f = do
App.debug "Relaying" App.debug "Relaying"
(\from -> f $ Relay {from, message}) =<< asks App.key (\from -> f $ Relay {from, message}) =<< asks App.playerID
receive :: App.T FromClient receive :: App.T FromClient
receive = do receive = do
@ -70,7 +70,7 @@ get =
update :: T update :: T
update = Update {alone = [], paired = []} update = Update {alone = [], paired = []}
notifyPlayers :: Game.T -> [KoiKoi.Action] -> App.T () notifyPlayers :: KoiKoi.Game -> [KoiKoi.Action] -> App.T ()
notifyPlayers game logs = notifyPlayers game logs =
forM_ (keys $ KoiKoi.scores game) $ \k -> forM_ (keys $ KoiKoi.scores game) $ \k ->
sendTo [k] $ Game {game = Game.export k game, logs} sendTo [k] $ Game {game = Game.export k game, logs}

View file

@ -2,9 +2,7 @@
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Server ( module Server (
T(..) T(..)
, disconnect , disconnect
@ -23,17 +21,16 @@ import qualified Data.Map as Map (empty)
import Data.Set (Set, member) import Data.Set (Set, member)
import qualified Data.Set as Set (delete, empty, insert) import qualified Data.Set as Set (delete, empty, insert)
import Data.Text (Text) import Data.Text (Text)
import Hanafuda.KoiKoi (PlayerKey) import Hanafuda.KoiKoi (Game, GameID, PlayerID)
import Hanafuda.Message (PlayerStatus(..), Room) import Hanafuda.Message (PlayerStatus(..), Room)
import qualified Data (RW(..)) import qualified Data (RW(..))
import qualified Game (Key, T)
import qualified Player (T(..)) import qualified Player (T(..))
import qualified Session (Status(..), T(..), Update) import qualified Session (Status(..), T(..), Update)
type Names = Set Text type Names = Set Text
type Players = Map PlayerKey Player.T type Players = Map PlayerID Player.T
type Sessions = Map PlayerKey Session.T type Sessions = Map PlayerID Session.T
type Games = Map Game.Key Game.T type Games = Map GameID Game
data T = T { data T = T {
names :: Names names :: Names
, players :: Players , players :: Players
@ -57,23 +54,17 @@ instance Data.RW Games T where
get = games get = games
set games server = server {games} set games server = server {games}
export :: Sessions -> PlayerKey -> Player.T -> PlayerStatus export :: Sessions -> PlayerID -> Player.T -> PlayerStatus
export sessions key player = PlayerStatus (Player.name player, alone) export sessions playerID player = PlayerStatus (Player.name player, alone)
where where
alone = alone =
case Session.status (sessions ! key) of case Session.status (sessions ! playerID) of
Session.LoggedIn True -> True Session.LoggedIn True -> True
_ -> False _ -> False
room :: T -> Room room :: T -> Room
room (T {players, sessions}) = mapWithKey (export sessions) players room (T {players, sessions}) = mapWithKey (export sessions) players
{-
instance ToJSON T where
toJSON (T {players, sessions}) = toJSON $ mapWithKey (export sessions) players
toEncoding (T {players, sessions}) = toEncoding $ mapWithKey (export sessions) players
-}
new :: T new :: T
new = T { new = T {
names = Set.empty names = Set.empty
@ -84,39 +75,39 @@ new = T {
register :: forall a b. (Enum a, Ord a, Data.RW (Map a b) T) => b -> T -> (T, a) register :: forall a b. (Enum a, Ord a, Data.RW (Map a b) T) => b -> T -> (T, a)
register x server = register x server =
let key = maybe (toEnum 0) (\(n, _) -> succ n) $ lookupMax $ (Data.get server :: Map a b) in let playerID = maybe (toEnum 0) (\(n, _) -> succ n) $ lookupMax $ (Data.get server :: Map a b) in
(Data.update (insert key x) server, key) (Data.update (insert playerID x) server, playerID)
get :: forall a b. (Ord a, Data.RW (Map a b) T) => a -> T -> b get :: forall a b. (Ord a, Data.RW (Map a b) T) => a -> T -> b
get key server = (Data.get server :: Map a b) ! key get playerID server = (Data.get server :: Map a b) ! playerID
update :: forall a b. (Ord a, Data.RW (Map a b) T) => a -> (b -> b) -> T -> T update :: forall a b. (Ord a, Data.RW (Map a b) T) => a -> (b -> b) -> T -> T
update key updator = update playerID updator =
Data.update (adjust updator key :: Map a b -> Map a b) Data.update (adjust updator playerID :: Map a b -> Map a b)
disconnect :: PlayerKey -> T -> T disconnect :: PlayerID -> T -> T
disconnect key = disconnect playerID =
Data.update (delete key :: Sessions -> Sessions) . logOut key Data.update (delete playerID :: Sessions -> Sessions) . logOut playerID
endGame :: Game.Key -> T -> T endGame :: GameID -> T -> T
endGame key = endGame playerID =
Data.update (delete key :: Games -> Games) Data.update (delete playerID :: Games -> Games)
logIn :: Text -> PlayerKey -> T -> Either String T logIn :: Text -> PlayerID -> T -> Either String T
logIn name key server = logIn name playerID server =
Data.update (Set.insert name) . Data.update (Set.insert name) .
Data.update (insert key $ Player.T {Player.name}) . Data.update (insert playerID $ Player.T {Player.name}) .
update key (Data.set $ Session.LoggedIn True :: Session.Update) <$> update playerID (Data.set $ Session.LoggedIn True :: Session.Update) <$>
if name `member` names server if name `member` names server
then Left "This name is already registered" then Left "This name is already registered"
else Right server else Right server
logOut :: PlayerKey -> T -> T logOut :: PlayerID -> T -> T
logOut key server = logOut playerID server =
maybe maybe
server server
(\player -> (\player ->
Data.update (delete key :: Players -> Players) $ Data.update (delete playerID :: Players -> Players) $
update key (Data.set $ Session.LoggedIn False :: Session.Update) $ update playerID (Data.set $ Session.LoggedIn False :: Session.Update) $
Data.update (Set.delete $ Player.name player :: Names -> Names) server) Data.update (Set.delete $ Player.name player :: Names -> Names) server)
(players server !? key) (players server !? playerID)

View file

@ -1,6 +1,5 @@
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DeriveGeneric #-}
module Session ( module Session (
Status(..) Status(..)
, T(..) , T(..)
@ -9,22 +8,15 @@ module Session (
) where ) where
import Network.WebSockets (Connection) import Network.WebSockets (Connection)
import Data.Aeson (ToJSON(..), genericToEncoding) import Hanafuda.KoiKoi (GameID, PlayerID)
import GHC.Generics (Generic)
import Hanafuda.KoiKoi (PlayerKey)
import qualified JSON (singleLCField)
import qualified Data (RW(..)) import qualified Data (RW(..))
import qualified Game (Key)
data Status = data Status =
LoggedIn Bool LoggedIn Bool
| Answering PlayerKey | Answering PlayerID
| Waiting PlayerKey | Waiting PlayerID
| Playing Game.Key | Playing GameID
deriving (Show, Generic) deriving (Show)
instance ToJSON Status where
toEncoding = genericToEncoding JSON.singleLCField
data T = T { data T = T {
connection :: Connection connection :: Connection