First registration/login interface
This commit is contained in:
141
Sources/App/Model/Database.swift
Normal file
141
Sources/App/Model/Database.swift
Normal file
@ -0,0 +1,141 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
17
Sources/App/Model/Table.swift
Normal file
17
Sources/App/Model/Table.swift
Normal file
@ -0,0 +1,17 @@
|
||||
import Foundation
|
||||
|
||||
struct TableInfo: Codable {
|
||||
|
||||
let id: String
|
||||
|
||||
let name: String
|
||||
|
||||
var players: [String]
|
||||
}
|
||||
|
||||
extension TableInfo: Comparable {
|
||||
|
||||
static func < (lhs: TableInfo, rhs: TableInfo) -> Bool {
|
||||
lhs.name < rhs.name
|
||||
}
|
||||
}
|
9
Sources/App/Model/User.swift
Normal file
9
Sources/App/Model/User.swift
Normal file
@ -0,0 +1,9 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
struct User: Codable {
|
||||
|
||||
let name: String
|
||||
|
||||
let passwordHash: Data
|
||||
}
|
Reference in New Issue
Block a user