Schafkopf-Server/Sources/App/Model/Database.swift
2021-11-27 11:59:13 +01:00

142 lines
4.0 KiB
Swift

import Foundation
import Crypto
let playerPerTable = 4
final class Database {
/// A mapping between usernames and their password hashes
private var userPasswordHashes = [String: String]()
/// A mapping between usernames and generated access tokens for a session
private var authTokenForUser = [String: String]()
/// A reverse mapping between generated access tokens and usernames
private var userForToken = [String: String]()
/// A list of table ids for public games
private var publicTables = Set<String>()
/// A mapping from table id to table name (for all tables)
private var tableNames = [String: String]()
/// A mapping from table id to participating players
private var tablePlayers = [String: [String]]()
/// A reverse list of players and their table id
private var playerTables = [String: String]()
init() {
}
/**
Check if a user exists.
- Parameter name: The name of the user
- Returns: true, if the user exists
*/
func has(user: String) -> Bool {
userPasswordHashes[user] != nil
}
/**
Get the password hash for a user, if the user exists.
- Parameter name: The name of the user
- Returns: The stored password hash, if the user exists
*/
func hash(ofUser name: String) -> String? {
userPasswordHashes[name]
}
/**
Create a new user and assign an access token.
- Parameter name: The name of the new user
- Parameter hash: The password hash of the user
- Returns: The generated access token for the session
*/
func add(user name: String, hash: String) -> String {
self.userPasswordHashes[name] = hash
return startSession(forUser: name)
}
/**
Start a new session for an existing user.
- Parameter name: The user name
- Returns: The generated access token for the session
*/
func startSession(forUser name: String) -> String {
let token = newToken()
self.authTokenForUser[name] = token
self.userForToken[token] = name
return token
}
/**
Get the user for a session token.
- Parameter token: The access token for the user
- Returns: The name of the user, if it exists
*/
func user(forToken token: String) -> String? {
userForToken[token]
}
func tableExists(named name: String) -> Bool {
tableNames.contains { $0.value == name }
}
func tableExists(withId id: String) -> Bool {
tableNames[id] != nil
}
func tableIsFull(withId id: String) -> Bool {
tablePlayers[id]!.count < playerPerTable
}
/**
Create a new table with optional players.
- Parameter name: The name of the table
- Parameter players: The player creating the table
- Parameter visible: Indicates that this is a game joinable by everyone
- Returns: The table id
*/
func createTable(named name: String, player: String, visible: Bool) -> String {
let tableId = newToken()
tableNames[tableId] = tableId
tablePlayers[tableId] = [player]
playerTables[player] = tableId
if visible {
publicTables.insert(tableId)
}
return tableId
}
func getPublicTableInfos() -> [TableInfo] {
publicTables.map { tableId in
TableInfo(id: tableId, name: tableNames[tableId]!, players: tablePlayers[tableId]!)
}.sorted()
}
func join(tableId: String, player: String) {
tablePlayers[tableId]!.append(player)
if let oldTable = playerTables[tableId] {
remove(player: player, fromTable: oldTable)
}
playerTables[tableId] = tableId
}
func remove(player: String, fromTable tableId: String) {
tablePlayers[tableId] = tablePlayers[tableId]?.filter { $0 != player }
}
/**
Create a new access token.
*/
private func newToken() -> String {
Crypto.SymmetricKey.init(size: .bits128).withUnsafeBytes {
$0.hexEncodedString()
}
}
}