Schafkopf-Server/Sources/App/Model/TableManagement.swift
2021-11-30 20:56:04 +01:00

131 lines
3.9 KiB
Swift

import Foundation
import WebSocketKit
import Vapor
let maximumPlayersPerTable = 4
typealias TableId = String
typealias TableName = String
final class TableManagement {
/// A list of table ids for public games
private var publicTables = Set<TableId>()
/// A mapping from table id to table name (for all tables)
private var tableNames = [TableId: TableName]()
/// A mapping from table id to participating players
private var tablePlayers = [TableId: [PlayerName]]()
/// A reverse list of players and their table id
private var playerTables = [PlayerName: TableId]()
private var playerConnections = [PlayerName : WebSocket]()
init() {
}
/**
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: TableName, player: PlayerName, visible: Bool) -> TableId {
let tableId = TableId.newToken()
tableNames[tableId] = name
tablePlayers[tableId] = [player]
playerTables[player] = tableId
if visible {
publicTables.insert(tableId)
}
return tableId
}
func getPublicTableInfos() -> [TableInfo] {
publicTables.map(tableInfo).sorted()
}
private func tableInfo(id tableId: TableId) -> TableInfo {
let players = tablePlayers[tableId]!
let connected = players.map { playerConnections[$0] != nil }
return TableInfo(
id: tableId,
name: tableNames[tableId]!,
players: players,
connected: connected)
}
func currentTableOfPlayer(named player: PlayerName) -> TableId? {
playerTables[player]
}
/**
Join a table.
- Returns: The result of the join operation
*/
func join(tableId: TableId, player: PlayerName) -> JoinTableResult {
guard var players = tablePlayers[tableId] else {
return .tableNotFound
}
guard !players.contains(player) else {
return .success
}
guard players.count < maximumPlayersPerTable else {
return .tableIsFull
}
players.append(player)
if let oldTable = playerTables[tableId] {
remove(player: player, fromTable: oldTable)
}
tablePlayers[tableId] = players
playerTables[tableId] = tableId
return .success
}
func remove(player: PlayerName, fromTable tableId: TableId) {
tablePlayers[tableId] = tablePlayers[tableId]?.filter { $0 != player }
// TODO: End connection for removed user
// TODO: End game if needed, send info to remaining players
}
func remove(player: PlayerName) {
guard let tableId = playerTables[player] else {
return
}
remove(player: player, fromTable: tableId)
}
func connect(player: PlayerName, using socket: WebSocket) -> Bool {
guard let tableId = playerTables[player] else {
return false
}
guard let players = tablePlayers[tableId] else {
print("Player \(player) was assigned to missing table \(tableId.prefix(5))")
playerTables[player] = nil
return false
}
guard players.contains(player) else {
print("Player \(player) was assigned to table \(tableId.prefix(5)) where it wasn't listed")
return false
}
playerConnections[player] = socket
let tableInfo = self.tableInfo(id: tableId)
// Notify other players at table about changes
players
.compactMap { playerConnections[$0] }
.forEach { $0.send(.tableInfo, data: tableInfo) }
return true
}
func disconnect(player: PlayerName) {
fatalError()
}
}