packages feed

thock-0.2.0.0: src/Online.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TemplateHaskell #-}

-- | This module has shared types and functions for communication between the websocket client and server.
module Online where

import Control.Lens (makeFieldsNoPrefix)
import Data.Aeson (FromJSON, ToJSON, decode, encode)
import Data.Maybe (fromMaybe)
import qualified Data.Text as T
import GHC.Generics (Generic)
import qualified Network.WebSockets as WS
import Quotes (Quote)
import Thock (GameState, HasRoomId (..), HasUsername (..), RoomId)

-- | The state of a client in a waiting room
data RoomClientState = RoomClientState
  { _username :: T.Text,
    _isReady :: Bool
  }
  deriving (Generic)

makeFieldsNoPrefix ''RoomClientState

instance FromJSON RoomClientState

instance ToJSON RoomClientState

-- | The state of a client in an online game
data GameClientState = GameClientState
  { _username :: T.Text,
    _progress :: Float,
    _wpm :: Double
  }
  deriving (Generic)

makeFieldsNoPrefix ''GameClientState

instance FromJSON GameClientState

instance ToJSON GameClientState

-- | A waiting room client with a state and connection to the server
data RoomClient = RoomClient
  { _state :: RoomClientState,
    _connection :: WS.Connection
  }

makeFieldsNoPrefix ''RoomClient

-- | An in-game client with a state and connection to the server
data GameClient = GameClient
  { _state :: GameClientState,
    _connection :: WS.Connection
  }

makeFieldsNoPrefix ''GameClient

-- | A message that a client sends to the server to update their state
data ClientMessage
  = RoomClientUpdate RoomClientState
  | GameClientUpdate GameClientState
  | BackToLobby T.Text
  deriving (Generic)

instance FromJSON ClientMessage

instance ToJSON ClientMessage

-- | A message from the server that updates a client on the state of other clients
data ServerMessage
  = RoomUpdate [RoomClientState]
  | GameUpdate [GameClientState]
  | StartGame Quote [GameClientState]
  deriving (Generic)

instance FromJSON ServerMessage

instance ToJSON ServerMessage

-- | Custom event that triggers when the client receives a 'ServerMessage'
newtype ConnectionTick = ConnectionTick ServerMessage

-- | The state of a player waiting to start an online game
data WaitingRoomState = WaitingRoomState
  { -- | Which room the player is in
    _roomId :: RoomId,
    -- | Information about the player
    _localState :: RoomClientState,
    -- | The player's connection to the server
    _connection :: WS.Connection,
    -- | The states of other players
    _otherPlayers :: [RoomClientState]
  }

makeFieldsNoPrefix ''WaitingRoomState

-- | The state of a player's online game
data OnlineGameState = OnlineGameState
  { -- | The state of the player's personal game
    _localGame :: GameState,
    -- | Room to return to after the game is done
    _roomId :: RoomId,
    -- | The player's username
    _username :: T.Text,
    -- | The player's connection to the server
    _connection :: WS.Connection,
    -- | The states of other players
    _otherPlayers :: [GameClientState]
  }

makeFieldsNoPrefix ''OnlineGameState

-- | The current status of the online connection
data Online
  = WaitingRoom WaitingRoomState
  | OnlineGame OnlineGameState

-- | Sends the given data over the connection as text using 'encode'
sendJsonData :: ToJSON a => WS.Connection -> a -> IO ()
sendJsonData conn a = WS.sendTextData conn (encode a)

-- | Receives JSON text data from the connection and tries to 'decode' it into the given type.
-- Produces an error if the JSON fails to decode (malformed or wrong type).
receiveJsonData :: FromJSON a => WS.Connection -> IO a
receiveJsonData conn = fromMaybe (error "could not decode JSON message into desired type") . decode <$> WS.receiveData conn