import Foundation typealias PlayerName = String typealias PasswordHash = String typealias SessionToken = String /// Manages player registration, session tokens and password hashes final class PlayerManagement { /// A mapping between player name and their password hashes private var playerPasswordHashes = [PlayerName: PasswordHash]() /// A mapping between player name and generated access tokens for a session private var sessionTokenForPlayer = [PlayerName: SessionToken]() /// A reverse mapping between generated access tokens and player name private var playerNameForToken = [SessionToken: PlayerName]() init() { } /** Check if a player exists. - Parameter name: The name of the player - Returns: true, if the player exists */ func hasRegisteredPlayer(named user: PlayerName) -> Bool { playerPasswordHashes[user] != nil } /** Get the password hash for a player, if the player exists. - Parameter name: The name of the player - Returns: The stored password hash, if the player exists */ func passwordHash(ofRegisteredPlayer name: PlayerName) -> PasswordHash? { playerPasswordHashes[name] } /** Create a new player and assign an access token. - Parameter name: The name of the new player - Parameter hash: The password hash of the player - Returns: The generated access token for the session */ func registerPlayer(named name: PlayerName, hash: PasswordHash) -> SessionToken? { guard !hasRegisteredPlayer(named: name) else { return nil } self.playerPasswordHashes[name] = hash return startNewSessionForRegisteredPlayer(named: name) } /** Delete a player - Parameter name: The name of the player to delete. - Returns: The session token of the current player, if one exists */ func deletePlayer(named name: PlayerName) -> SessionToken? { playerPasswordHashes.removeValue(forKey: name) guard let sessionToken = sessionTokenForPlayer.removeValue(forKey: name) else { return nil } playerNameForToken.removeValue(forKey: sessionToken) return sessionToken } func isValid(sessionToken token: SessionToken) -> Bool { playerNameForToken[token] != nil } func sessionToken(forPlayer player: PlayerName) -> SessionToken? { sessionTokenForPlayer[player] } /** Start a new session for an existing player. - Parameter name: The player name - Returns: The generated access token for the session */ func startNewSessionForRegisteredPlayer(named name: PlayerName) -> SessionToken { let token = SessionToken.newToken() self.sessionTokenForPlayer[name] = token self.playerNameForToken[token] = name return token } func endSession(forSessionToken token: SessionToken) -> PlayerName? { guard let player = playerNameForToken.removeValue(forKey: token) else { return nil } sessionTokenForPlayer.removeValue(forKey: player) return player } /** Get the player for a session token. - Parameter token: The access token for the player - Returns: The name of the player, if it exists */ func registeredPlayerExists(withSessionToken token: SessionToken) -> PlayerName? { playerNameForToken[token] } }