142 lines
4.0 KiB
Swift
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()
|
||
|
}
|
||
|
}
|
||
|
}
|