battleplace (empty) → 0.1.0.0
raw patch · 8 files changed
+646/−0 lines, 8 filesdep +aesondep +basedep +bytestring
Dependencies added: aeson, base, bytestring, cereal, data-default, hashable, memory, servant, text, vector
Files
- BattlePlace/Rating.hs +16/−0
- BattlePlace/Skill/Glicko/Types.hs +46/−0
- BattlePlace/Token/Types.hs +24/−0
- BattlePlace/Util.hs +35/−0
- BattlePlace/WebApi.hs +226/−0
- BattlePlace/WebApi/Types.hs +243/−0
- LICENSE +21/−0
- battleplace.cabal +35/−0
+ BattlePlace/Rating.hs view
@@ -0,0 +1,16 @@+{-|+Module: BattlePlace.Rating+Description: Rating type.+License: MIT+-}++{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module BattlePlace.Rating+ ( Rating(..)+ ) where++import qualified Data.Aeson as J++-- | User rating.+newtype Rating = Rating Double deriving (Eq, Ord, J.FromJSON, J.ToJSON)
+ BattlePlace/Skill/Glicko/Types.hs view
@@ -0,0 +1,46 @@+{-|+Module: BattlePlace.Skill.Glicko.Types+Description: Types for Glicko ratings.+License: MIT+-}++{-# LANGUAGE DeriveGeneric #-}++module BattlePlace.Skill.Glicko.Types+ ( Skill(..)+ , ratingFromSkill+ ) where++import qualified Data.Aeson as J+import Data.Default+import GHC.Generics(Generic)++import BattlePlace.Rating+import BattlePlace.Util++data Skill = Skill+ { skill_rating :: {-# UNPACK #-} !Double+ , skill_deviation :: {-# UNPACK #-} !Double+ , skill_volatility :: {-# UNPACK #-} !Double+ } deriving Generic++-- default skill+instance Default (Skill) where+ def = Skill+ { skill_rating = 1500+ , skill_deviation = 350+ , skill_volatility = 0.06+ }++instance J.FromJSON (Skill) where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON (Skill) where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++-- | Conservative rating for skill.+ratingFromSkill :: Skill -> Rating+ratingFromSkill Skill+ { skill_rating = rating+ , skill_deviation = deviation+ } = Rating $ rating - deviation * 2
+ BattlePlace/Token/Types.hs view
@@ -0,0 +1,24 @@+{-|+Module: BattlePlace.Token.Types+Description: Token types.+License: MIT+-}++{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module BattlePlace.Token.Types+ ( InternalToken(..)+ , Ticket(..)+ ) where++import qualified Data.Aeson as J+import qualified Data.Text as T+import Servant.API++-- | Internal token.+-- Token is encrypted, and contains some internal information.+newtype InternalToken a = InternalToken T.Text deriving (J.FromJSON, J.ToJSON, FromHttpApiData, ToHttpApiData)++-- | Ticket.+-- Ticket doesn't contain information, it's just an ID.+newtype Ticket = Ticket T.Text deriving (J.FromJSON, J.ToJSON)
+ BattlePlace/Util.hs view
@@ -0,0 +1,35 @@+{-|+Module: BattlePlace.Util+Description: General utilities.+License: MIT+-}++{-# LANGUAGE LambdaCase #-}++module BattlePlace.Util+ ( jsonOptions+ , jsonOptionsWithTag+ ) where++import qualified Data.Aeson as J++jsonOptions :: J.Options+jsonOptions = jsonOptionsWithTag "status"++jsonOptionsWithTag :: String -> J.Options+jsonOptionsWithTag tag = J.defaultOptions+ { J.fieldLabelModifier = dropBeforeUnderscore+ , J.constructorTagModifier = dropBeforeUnderscore+ , J.sumEncoding = J.TaggedObject+ { J.tagFieldName = tag+ , J.contentsFieldName = "contents"+ }+ }++dropBeforeUnderscore :: String -> String+dropBeforeUnderscore = \case+ x : xs -> case x of+ '_' -> xs+ _ -> dropBeforeUnderscore xs+ [] -> []+
+ BattlePlace/WebApi.hs view
@@ -0,0 +1,226 @@+{-|+Module: BattlePlace.WebApi+Description: Web API definitions.+License: MIT+-}++{-# LANGUAGE DataKinds, DeriveGeneric, GeneralizedNewtypeDeriving, TypeOperators #-}++module BattlePlace.WebApi+ ( WebApi+ , ClientAuthRequest(..)+ , ClientAuthResponse(..)+ , MatchRequest(..)+ , MatchResponse(..)+ , MatchStatusResponse(..)+ , MatchStatusReason(..)+ , MatchTeam(..)+ , MatchPlayer(..)+ , MatchServer(..)+ , MatchServerInfo(..)+ , SessionResultRequest(..)+ , ServerMatchRequest(..)+ , ServerMatchResponse(..)+ , ServerSessionResultRequest(..)+ , ServerSession(..)+ ) where++import qualified Data.Aeson as J+import qualified Data.Text as T+import qualified Data.Vector as V+import qualified Data.Vector.Unboxed as VU+import GHC.Generics(Generic)+import Servant.API++import BattlePlace.Token.Types+import BattlePlace.Util+import BattlePlace.WebApi.Types++type WebApi = "v1a" :>+ ( "client" :>+ ( "auth" :> ReqBody '[JSON] ClientAuthRequest :> Post '[JSON] ClientAuthResponse+ :<|> "match" :>+ ( ReqBody '[JSON] MatchRequest :> Post '[JSON] MatchResponse+ :<|> Capture "matchToken" (InternalToken MatchToken) :> Get '[JSON] MatchStatusResponse+ )+ :<|> "session" :> Capture "sessionToken" (InternalToken SessionToken) :>+ ( "result" :> ReqBody '[JSON] SessionResultRequest :> Post '[JSON] ()+ )+ )+ :<|> "server" :>+ ( "match" :> ReqBody '[JSON] ServerMatchRequest :> Post '[JSON] ServerMatchResponse+ :<|> "session" :> Capture "serverSessionToken" (InternalToken ServerSessionToken) :>+ ( "result" :> ReqBody '[JSON] ServerSessionResultRequest :> Post '[JSON] ()+ )+ )+ )++data ClientAuthRequest = ClientAuthRequest+ { clientAuthRequest_projectId :: {-# UNPACK #-} !ProjectId+ , clientAuthRequest_auth :: !Auth+ } deriving Generic+instance J.FromJSON ClientAuthRequest where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON ClientAuthRequest where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++data ClientAuthResponse+ = ClientAuthResponse_authenticated+ { clientAuthResponse_clientToken :: !(InternalToken ClientToken)+ , clientAuthResponse_name :: !T.Text+ , clientAuthResponse_pictureUrl :: !T.Text+ }+ | ClientAuthResponse_notAuthenticated+ { clientAuthResponse_error :: !T.Text+ }+ deriving Generic+instance J.FromJSON ClientAuthResponse where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON ClientAuthResponse where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++data MatchRequest = MatchRequest+ { matchRequest_clientToken :: !(InternalToken ClientToken)+ , matchRequest_teamSizes :: !(VU.Vector MatchTeamSize)+ , matchRequest_maxMatchTime :: {-# UNPACK #-} !Int+ , matchRequest_matchTag :: !(Maybe MatchTag)+ , matchRequest_info :: !(Maybe MatchPlayerInfo)+ } deriving Generic+instance J.FromJSON MatchRequest where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON MatchRequest where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++data MatchResponse = MatchResponse+ { matchResponse_matchToken :: !(InternalToken MatchToken)+ } deriving Generic+instance J.FromJSON MatchResponse where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON MatchResponse where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++data MatchStatusResponse+ = MatchStatusResponse_notFound+ | MatchStatusResponse_inProgress+ | MatchStatusResponse_matched+ { matchStatusResponse_sessionId :: !SessionId+ , matchStatusResponse_sessionToken :: !(InternalToken SessionToken)+ , matchStatusResponse_teams :: !(V.Vector MatchTeam)+ , matchStatusResponse_teamIndex :: {-# UNPACK #-} !Int+ , matchStatusResponse_mateIndex :: {-# UNPACK #-} !Int+ , matchStatusResponse_server :: !(Maybe MatchServer)+ }+ | MatchStatusResponse_failed+ { matchStatusResponse_reason :: !MatchStatusReason+ }+ -- | Match status was cleaned.+ -- Normallly this status should not be visible to clients, it's here just in case.+ | MatchStatusResponse_cleaned+ deriving Generic+instance J.FromJSON MatchStatusResponse where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON MatchStatusResponse where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++-- | Reason of match failure.+data MatchStatusReason+ -- | Failed to make a match in a specified time.+ = MatchStatusReason_timedOut+ -- | Match was made, but no server is available (and use of server is mandatory).+ | MatchStatusReason_noServer+ -- | Matching was explicitly cancelled by user.+ | MatchStatusReason_cancelled+ deriving Generic+instance J.FromJSON MatchStatusReason where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON MatchStatusReason where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++newtype MatchTeam = MatchTeam (V.Vector MatchPlayer) deriving (J.FromJSON, J.ToJSON)++data MatchPlayer = MatchPlayer+ { matchPlayer_info :: !MatchPlayerInfo+ , matchPlayer_ourTicket :: !(Maybe Ticket)+ , matchPlayer_theirTicket :: !(Maybe Ticket)+ } deriving Generic+instance J.FromJSON MatchPlayer where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON MatchPlayer where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++data MatchServer = MatchServer+ { matchServer_info :: !MatchServerInfo+ , matchServer_ourTicket :: !Ticket+ , matchServer_theirTicket :: !Ticket+ } deriving Generic+instance J.FromJSON MatchServer where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON MatchServer where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++newtype MatchServerInfo = MatchServerInfo T.Text deriving (J.FromJSON, J.ToJSON)++data SessionResultRequest+ = SessionResultRequest_finished+ { sessionResultRequest_ranks :: !(V.Vector Int)+ }+ | SessionResultRequest_cancelled+ deriving Generic+instance J.FromJSON SessionResultRequest where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON SessionResultRequest where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++data ServerMatchRequest = ServerMatchRequest+ { serverMatchRequest_projectId :: {-# UNPACK #-} !ProjectId+ , serverMatchRequest_projectServerToken :: !ProjectServerToken+ , serverMatchRequest_maxSessionsCount :: {-# UNPACK #-} !Int+ , serverMatchRequest_info :: !MatchServerInfo+ , serverMatchRequest_timeout :: !(Maybe Int)+ } deriving Generic+instance J.FromJSON ServerMatchRequest where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON ServerMatchRequest where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++data ServerMatchResponse = ServerMatchResponse+ { serverMatchResponse_sessions :: !(V.Vector ServerSession)+ } deriving Generic+instance J.FromJSON ServerMatchResponse where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON ServerMatchResponse where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++data ServerSessionResultRequest+ = ServerSessionResultRequest_finished+ { serverSessionResultRequest_ranks :: !(V.Vector Int)+ }+ | ServerSessionResultRequest_cancelled+ deriving Generic+instance J.FromJSON ServerSessionResultRequest where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON ServerSessionResultRequest where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++data ServerSession = ServerSession+ { serverSession_sessionId :: !SessionId+ , serverSession_serverSessionToken :: !(InternalToken ServerSessionToken)+ , serverSession_teams :: !(V.Vector MatchTeam)+ } deriving Generic+instance J.FromJSON ServerSession where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON ServerSession where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions
+ BattlePlace/WebApi/Types.hs view
@@ -0,0 +1,243 @@+{-|+Module: BattlePlace.WebApi.Types+Description: Web API types.+License: MIT+-}++{-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving, LambdaCase, ViewPatterns #-}++module BattlePlace.WebApi.Types+ ( AccountId(..)+ , ProjectId(..)+ , Auth(..)+ , AuthType(..)+ , authTypeOf+ , Client(..)+ , ClientToken(..)+ , ProjectServerId(..)+ , ProjectServerToken(..)+ , MatchTeamSize+ , MatchTag(..)+ , MatchPlayerInfo(..)+ , MatchToken(..)+ , SessionToken(..)+ , SessionId(..)+ , ServerSessionToken(..)+ , Identified(..)+ , Base64ByteString(..)+ , Base64Word64(..)+ , StrWord64(..)+ ) where++import Control.Monad+import qualified Data.Aeson as J+import qualified Data.Aeson.Types as J+import qualified Data.ByteArray as BA+import qualified Data.ByteArray.Encoding as BA+import qualified Data.ByteString as B+import Data.Either+import Data.Hashable+import qualified Data.Serialize as S+import Data.String+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import Data.Word+import Foreign.Storable+import GHC.Generics(Generic)+import Servant.API++import BattlePlace.Util++-- | Account id.+-- At the moment it's just itch user id, but that may change.+newtype AccountId = AccountId Base64Word64 deriving (J.FromJSON, J.ToJSON, FromHttpApiData)++-- | Project id.+newtype ProjectId = ProjectId Base64Word64 deriving (Eq, Hashable, J.FromJSON, J.ToJSON, FromHttpApiData)++data Auth+ = Auth_itchJwtToken+ { auth_itchJwtToken :: !T.Text+ }+ | Auth_steamEncryptedTicket+ { auth_steamEncryptedTicket :: !T.Text+ }+ | Auth_testKey+ { auth_testKey :: !T.Text+ , auth_testId :: !StrWord64+ }+ deriving Generic+instance J.FromJSON Auth where+ parseJSON = J.genericParseJSON jsonOptions+ { J.sumEncoding = J.UntaggedValue+ }+instance J.ToJSON Auth where+ toJSON = J.genericToJSON jsonOptions+ { J.sumEncoding = J.UntaggedValue+ }+ toEncoding = J.genericToEncoding jsonOptions+ { J.sumEncoding = J.UntaggedValue+ }++-- | Auth type (for logging).+data AuthType+ = AuthType_itchJwtToken+ | AuthType_steamEncryptedTicket+ | AuthType_testKey+ deriving Generic+instance J.FromJSON AuthType where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON AuthType where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++authTypeOf :: Auth -> AuthType+authTypeOf = \case+ Auth_itchJwtToken {} -> AuthType_itchJwtToken+ Auth_steamEncryptedTicket {} -> AuthType_steamEncryptedTicket+ Auth_testKey {} -> AuthType_testKey++data Client+ = Client_itch+ { client_itchUserId :: {-# UNPACK #-} !StrWord64+ }+ | Client_steam+ { client_steamId :: {-# UNPACK #-} !StrWord64+ }+ | Client_test+ { client_testId :: {-# UNPACK #-} !StrWord64+ }+ deriving (Eq, Generic)+instance Hashable Client+instance J.FromJSON Client where+ parseJSON = J.genericParseJSON $ jsonOptionsWithTag "type"+instance J.ToJSON Client where+ toJSON = J.genericToJSON $ jsonOptionsWithTag "type"+ toEncoding = J.genericToEncoding $ jsonOptionsWithTag "type"++data ClientToken = ClientToken+ { clientToken_projectId :: {-# UNPACK #-} !ProjectId+ , clientToken_client :: !Client+ } deriving Generic+instance J.FromJSON ClientToken where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON ClientToken where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++-- | Project's server id.+newtype ProjectServerId = ProjectServerId Base64Word64 deriving (Eq, Hashable, J.FromJSON, J.FromJSONKey, J.ToJSON, J.ToJSONKey)++-- | Project's secret server token.+newtype ProjectServerToken = ProjectServerToken T.Text deriving (Eq, Hashable, J.FromJSON, J.ToJSON)++-- | Size of a team in match request.+type MatchTeamSize = Int++-- | Match tag in match request.+newtype MatchTag = MatchTag T.Text deriving (Eq, Hashable, Monoid, J.FromJSON, J.ToJSON)++-- | Opaque player info.+newtype MatchPlayerInfo = MatchPlayerInfo T.Text deriving (Monoid, J.FromJSON, J.ToJSON)++-- | Match token.+data MatchToken = MatchToken+ { matchToken_projectId :: {-# UNPACK #-} !ProjectId+ , matchToken_client :: !Client+ } deriving Generic+instance J.FromJSON MatchToken where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON MatchToken where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++-- | Session token.+data SessionToken = SessionToken+ { sessionToken_sessionId :: !SessionId+ , sessionToken_teamIndex :: {-# UNPACK #-} !Int+ , sessionToken_mateIndex :: {-# UNPACK #-} !Int+ , sessionToken_client :: !Client+ } deriving Generic+instance J.FromJSON SessionToken where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON SessionToken where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++-- | Session id.+newtype SessionId = SessionId Base64ByteString deriving (Eq, Hashable, J.FromJSON, J.ToJSON)++-- | Server session token.+newtype ServerSessionToken = ServerSessionToken+ { serverSessionToken_sessionId :: SessionId+ } deriving Generic+instance J.FromJSON ServerSessionToken where+ parseJSON = J.genericParseJSON jsonOptions+instance J.ToJSON ServerSessionToken where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++-- | Generic data type for id + object.+data Identified i a = Identified+ { identified_id :: !i+ , identified_info :: !a+ } deriving Generic+instance (J.FromJSON i, J.FromJSON a) => J.FromJSON (Identified i a) where+ parseJSON = J.genericParseJSON jsonOptions+instance (J.ToJSON i, J.ToJSON a) => J.ToJSON (Identified i a) where+ toJSON = J.genericToJSON jsonOptions+ toEncoding = J.genericToEncoding jsonOptions++-- | Implementation for parseJson using base64-serialized string.+base64ParseJsonStr :: S.Serialize a => T.Text -> J.Parser a+base64ParseJsonStr = \case+ (BA.convertFromBase BA.Base64URLUnpadded . T.encodeUtf8 -> Right (S.decode -> Right object)) -> return object+ _ -> fail "wrong base64 object"++-- | Implementation for toJson using base64-serialized string.+base64ToJsonStr :: S.Serialize a => a -> T.Text+base64ToJsonStr = T.decodeUtf8 . BA.convertToBase BA.Base64URLUnpadded . S.encode++-- | Implementation for parseUrlPiece using base64-serialized string.+base64ParseUrlPiece :: S.Serialize a => T.Text -> Either T.Text a+base64ParseUrlPiece = either (Left . T.pack) (either (Left . T.pack) Right . S.decode) . BA.convertFromBase BA.Base64URLUnpadded . T.encodeUtf8++-- | ByteString which serializes to JSON as base64 string.+newtype Base64ByteString = Base64ByteString B.ByteString deriving (Eq, Ord, Monoid, Hashable, BA.ByteArray, BA.ByteArrayAccess)+instance J.FromJSON Base64ByteString where+ parseJSON = either fail return . BA.convertFromBase BA.Base64URLUnpadded . T.encodeUtf8 <=< J.parseJSON+instance J.ToJSON Base64ByteString where+ toJSON = J.toJSON . T.decodeUtf8 . BA.convertToBase BA.Base64URLUnpadded++-- | Word64 which serializes to JSON as base64 string.+-- Useful because 64-bit integer is not representable in javascript.+newtype Base64Word64 = Base64Word64 Word64 deriving (Eq, Ord, Num, Storable, Hashable, S.Serialize, Read, Show)+instance J.FromJSON Base64Word64 where+ parseJSON = base64ParseJsonStr <=< J.parseJSON+instance J.FromJSONKey Base64Word64 where+ fromJSONKey = J.FromJSONKeyTextParser base64ParseJsonStr+instance J.ToJSON Base64Word64 where+ toJSON = J.toJSON . base64ToJsonStr+ toEncoding = J.toEncoding . base64ToJsonStr+instance J.ToJSONKey Base64Word64 where+ toJSONKey = J.toJSONKeyText base64ToJsonStr+instance FromHttpApiData Base64Word64 where+ parseUrlPiece = base64ParseUrlPiece+instance IsString Base64Word64 where+ fromString = fromRight (error "wrong base64 word") . J.parseEither J.parseJSON . J.String . T.pack++-- | Word64 which serializes to JSON as decimal string.+-- Useful because 64-bit integer is not representable in javascript.+newtype StrWord64 = StrWord64 Word64 deriving (Eq, Ord, Num, Storable, Hashable, S.Serialize, Read, Show)+instance J.FromJSON StrWord64 where+ parseJSON = f . reads <=< J.parseJSON where+ f = \case+ [(n, "")] -> return $ StrWord64 n+ _ -> fail "wrong number"+instance J.ToJSON StrWord64 where+ toJSON (StrWord64 n) = J.toJSON $ show n+instance FromHttpApiData StrWord64 where+ parseUrlPiece = f . reads <=< parseUrlPiece where+ f = \case+ [(n, "")] -> return $ StrWord64 n+ _ -> fail "wrong number"
+ LICENSE view
@@ -0,0 +1,21 @@+The MIT License (MIT)++Copyright (c) 2017-2018 Alexander Bich (aka Quyse Lert)++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+THE SOFTWARE.
+ battleplace.cabal view
@@ -0,0 +1,35 @@+name: battleplace+version: 0.1.0.0+synopsis: Core definitions for BattlePlace.io service+description: Core definitions for BattlePlace.io service+license: MIT+license-file: LICENSE+author: Alexander Bich <quyse0@gmail.com>+maintainer: Alexander Bich <quyse0@gmail.com>+copyright: (c) 2017-2018 Alexander Bich+category: Game+stability: experimental+build-type: Simple+cabal-version: >=1.10++library+ exposed-modules:+ BattlePlace.Util+ BattlePlace.Rating+ BattlePlace.Skill.Glicko.Types+ BattlePlace.Token.Types+ BattlePlace.WebApi+ BattlePlace.WebApi.Types+ other-modules:+ build-depends:+ aeson+ , base >= 4.5 && < 5+ , bytestring+ , cereal+ , data-default+ , hashable+ , memory+ , servant+ , text+ , vector+ default-language: Haskell2010