Follow move of 'logs' into PublicGame and start implementing re-sync protocol on the server side
This commit is contained in:
parent
ca30340aaa
commit
81ec84abaf
3 changed files with 40 additions and 23 deletions
|
@ -5,8 +5,12 @@ module Automaton (
|
||||||
|
|
||||||
import qualified App (Context(..), T, exec, get, player, update)
|
import qualified App (Context(..), T, exec, get, player, update)
|
||||||
import Control.Monad.Reader (asks)
|
import Control.Monad.Reader (asks)
|
||||||
import qualified Game (new, play)
|
import Data.Map ((!))
|
||||||
import qualified Hanafuda.Message as Message (FromClient(..), T(..))
|
import qualified Game (fromPublic, new, play, toPublic)
|
||||||
|
import qualified Hanafuda.KoiKoi as KoiKoi (Game(..))
|
||||||
|
import qualified Hanafuda.Message as Message (
|
||||||
|
FromClient(..), PublicGame(..), T(..)
|
||||||
|
)
|
||||||
import qualified Messaging (
|
import qualified Messaging (
|
||||||
broadcast, get, notifyPlayers, relay, send, sendTo
|
broadcast, get, notifyPlayers, relay, send, sendTo
|
||||||
)
|
)
|
||||||
|
@ -38,22 +42,31 @@ receive (Message.Tadaima {Message.myID, Message.name}) Nothing = do
|
||||||
|
|
||||||
receive (Message.Tadaima {}) (Just _) = sendError "You're already logged in"
|
receive (Message.Tadaima {}) (Just _) = sendError "You're already logged in"
|
||||||
|
|
||||||
receive invitation@(Message.Invitation {Message.to}) (Just _) =
|
receive invitation@(Message.Invitation {}) (Just _) = relay invitation
|
||||||
Messaging.relay invitation (Messaging.sendTo [to])
|
|
||||||
|
|
||||||
receive answer@(Message.Answer {Message.accept, Message.to}) (Just player) =
|
receive answer@(Message.Answer {Message.accept, Message.to}) (Just player) =
|
||||||
if accept
|
if accept
|
||||||
then do
|
then do
|
||||||
publicGames <- Game.new (Player.playerID player, to)
|
publicGames <- Game.new (Player.playerID player, to)
|
||||||
Messaging.relay answer (Messaging.sendTo [to])
|
Messaging.relay answer (Messaging.sendTo [to])
|
||||||
Messaging.notifyPlayers publicGames []
|
Messaging.notifyPlayers (publicGames, [])
|
||||||
else Messaging.relay answer (Messaging.sendTo [to])
|
else Messaging.relay answer (Messaging.sendTo [to])
|
||||||
|
|
||||||
receive (Message.Play {Message.move, Message.onGame}) (Just player) = do
|
receive (Message.Play {Message.move, Message.onGame}) (Just player) =
|
||||||
result <- Game.play (Player.playerID player) move onGame
|
Game.play (Player.playerID player) move onGame
|
||||||
case result of
|
>>= either sendError Messaging.notifyPlayers
|
||||||
Left message -> sendError message
|
|
||||||
Right (newGame, logs) -> Messaging.notifyPlayers newGame logs
|
receive sync@(Message.Sync {}) (Just _) = relay sync
|
||||||
|
receive yield@(Message.Yield {}) (Just _) = relay yield
|
||||||
|
|
||||||
|
receive (Message.Share {Message.gameSave}) (Just player) =
|
||||||
|
either sendError share =<< Game.fromPublic gameSave
|
||||||
|
where
|
||||||
|
logs = Message.logs gameSave
|
||||||
|
share game =
|
||||||
|
let recipientID = KoiKoi.nextPlayer game ! (Player.playerID player) in
|
||||||
|
Game.toPublic recipientID game logs
|
||||||
|
>>= Messaging.sendTo [recipientID] . Message.Game
|
||||||
|
|
||||||
receive message state =
|
receive message state =
|
||||||
sendError $ "Invalid message " ++ show message ++ " in " ++ showState
|
sendError $ "Invalid message " ++ show message ++ " in " ++ showState
|
||||||
|
@ -63,6 +76,9 @@ receive message state =
|
||||||
Nothing -> "disconnected state"
|
Nothing -> "disconnected state"
|
||||||
Just _ -> "connected state"
|
Just _ -> "connected state"
|
||||||
|
|
||||||
|
relay :: Message.FromClient -> App.T ()
|
||||||
|
relay message = Messaging.relay message (Messaging.sendTo [Message.to message])
|
||||||
|
|
||||||
sendError :: String -> App.T ()
|
sendError :: String -> App.T ()
|
||||||
sendError = Messaging.send . Message.Error
|
sendError = Messaging.send . Message.Error
|
||||||
|
|
||||||
|
|
16
src/Game.hs
16
src/Game.hs
|
@ -1,8 +1,9 @@
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
module Game (
|
module Game (
|
||||||
exportGame
|
fromPublic
|
||||||
, new
|
, new
|
||||||
, play
|
, play
|
||||||
|
, toPublic
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified App (T, get)
|
import qualified App (T, get)
|
||||||
|
@ -86,12 +87,13 @@ publicState coordinates game = PublicState {
|
||||||
, rounds = KoiKoi.rounds game
|
, rounds = KoiKoi.rounds game
|
||||||
}
|
}
|
||||||
|
|
||||||
exportGame :: PlayerID -> Game -> App.T PublicGame
|
toPublic :: PlayerID -> Game -> [KoiKoi.Action] -> App.T PublicGame
|
||||||
exportGame playerID game = do
|
toPublic playerID game logs = do
|
||||||
Keys.T {encrypt, sign} <- App.get Server.keys
|
Keys.T {encrypt, sign} <- App.get Server.keys
|
||||||
n <- lift newNonce
|
n <- lift newNonce
|
||||||
return $ PublicGame {
|
return $ PublicGame {
|
||||||
nonce = Saltine.encode n
|
nonce = Saltine.encode n
|
||||||
|
, logs
|
||||||
, playerHand = getHand playerID (KoiKoi.players game)
|
, playerHand = getHand playerID (KoiKoi.players game)
|
||||||
, private = secretbox encrypt n $ toJSON private
|
, private = secretbox encrypt n $ toJSON private
|
||||||
, public
|
, public
|
||||||
|
@ -124,8 +126,8 @@ merge public private = KoiKoi.Game {
|
||||||
, KoiKoi.rounds = rounds public
|
, KoiKoi.rounds = rounds public
|
||||||
}
|
}
|
||||||
|
|
||||||
importGame :: PublicGame -> App.T (Either String Game)
|
fromPublic :: PublicGame -> App.T (Either String Game)
|
||||||
importGame PublicGame {nonce, private, public, publicSignature} =
|
fromPublic PublicGame {nonce, private, public, publicSignature} =
|
||||||
App.get Server.keys >>= \(Keys.T {encrypt, sign}) -> return $ do
|
App.get Server.keys >>= \(Keys.T {encrypt, sign}) -> return $ do
|
||||||
check (signVerifyDetached (Keys.public sign) publicSignature (toJSON public))
|
check (signVerifyDetached (Keys.public sign) publicSignature (toJSON public))
|
||||||
`orDie` "The game state has been tampered with"
|
`orDie` "The game state has been tampered with"
|
||||||
|
@ -145,8 +147,8 @@ importGame PublicGame {nonce, private, public, publicSignature} =
|
||||||
play :: PlayerID -> KoiKoi.Move -> PublicGame -> App.T (Either String (Game, [KoiKoi.Action]))
|
play :: PlayerID -> KoiKoi.Move -> PublicGame -> App.T (Either String (Game, [KoiKoi.Action]))
|
||||||
play playerID move publicGame
|
play playerID move publicGame
|
||||||
| playing (public publicGame) == playerID = do
|
| playing (public publicGame) == playerID = do
|
||||||
imported <- importGame publicGame
|
result <- fromPublic publicGame
|
||||||
case imported of
|
case result of
|
||||||
Left errorMessage -> return $ Left errorMessage
|
Left errorMessage -> return $ Left errorMessage
|
||||||
Right game -> lift . runExceptT . runWriterT $ KoiKoi.play move game
|
Right game -> lift . runExceptT . runWriterT $ KoiKoi.play move game
|
||||||
| otherwise = return $ Left "Not your turn"
|
| otherwise = return $ Left "Not your turn"
|
||||||
|
|
|
@ -21,7 +21,7 @@ import Data.List (intercalate)
|
||||||
import Data.Map (elems, keys)
|
import Data.Map (elems, keys)
|
||||||
import Data.Maybe (maybeToList)
|
import Data.Maybe (maybeToList)
|
||||||
import qualified Data.Set as Set (fromList, member)
|
import qualified Data.Set as Set (fromList, member)
|
||||||
import qualified Game (exportGame)
|
import qualified Game (toPublic)
|
||||||
import qualified Hanafuda.KoiKoi as KoiKoi (Action, Game(..), PlayerID)
|
import qualified Hanafuda.KoiKoi as KoiKoi (Action, Game(..), PlayerID)
|
||||||
import Hanafuda.Message (FromClient(..), T(..))
|
import Hanafuda.Message (FromClient(..), T(..))
|
||||||
import qualified Hanafuda.Message as Message (T)
|
import qualified Hanafuda.Message as Message (T)
|
||||||
|
@ -78,8 +78,7 @@ get =
|
||||||
pong Ping = send Pong >> get
|
pong Ping = send Pong >> get
|
||||||
pong m = return m
|
pong m = return m
|
||||||
|
|
||||||
notifyPlayers :: KoiKoi.Game -> [KoiKoi.Action] -> App.T ()
|
notifyPlayers :: (KoiKoi.Game, [KoiKoi.Action]) -> App.T ()
|
||||||
notifyPlayers game logs =
|
notifyPlayers (game, logs) =
|
||||||
forM_ (keys $ KoiKoi.scores game) $ \k -> do
|
forM_ (keys $ KoiKoi.nextPlayer game) $ \k ->
|
||||||
state <- Game.exportGame k game
|
sendTo [k] . Game =<< Game.toPublic k game logs
|
||||||
sendTo [k] $ Game {state, logs}
|
|
||||||
|
|
Loading…
Reference in a new issue