def post(self): """ POST the required parameters to register the pocketing/unpocketing of a ball * `ball`: The ball that was pocketed/unpocketed """ gamemaster = self.get_current_user() ball = self.body['ball'] game_id = get_player(self.db_conn, gamemaster)["current_game_id"] game = Game(self.db_conn, "game_id", game_id) res = {"game_id": game_id} # Authenticate api_assert( game["gamemaster"] == gamemaster, 401, log_message="You are not the gamemaster of the current game" ) # If ball is already sunk, retable it if ball not in get_balls_on_table(self.db_conn, game_id): game = Game(self.db_conn, "game_id", game_id) # Look for ball in game's set of unclaimed balls if ball in game["orig_unclaimed_balls"]: game["unclaimed_balls"] += [ball] # If it's not there, look for it from each player's set else: for pname in game["players"]: p = Player(self.db_conn, "name", pname) if ball in p["orig_balls"]: p["balls"] += [ball] break res['message'] = "Ball {} was retabled.".format(ball) return res # Otherwise, sink the ball # Look for it in players' first for pname in Game(self.db_conn, "game_id", game_id)["players"]: p = get_player(self.db_conn, pname) if ball in p["balls"]: p["balls"] = list(set(p["balls"]) - {ball}) break else: # Remove ball from unclaimed g = Game(self.db_conn, "game_id", game_id) uballs = list(game["unclaimed_balls"]) uballs.remove(ball) game["unclaimed_balls"] = uballs res["message"] = "Ball {} was sunk.".format(ball) return res
def delete(self): """ DELETE to end the game; this endpoint should only be triggered if GameState indicates there is a winner. """ player_name = self.get_current_user() player = get_player(self.db_conn, player_name) game_id = player["current_game_id"] api_assert(game_id, 400, log_message="You are not currently in" " a game.") game = get_game(self.db_conn, game_id) with DBLock(): is_gamemaster = game["gamemaster"] == player_name gamemaster_has_closed = game["gamemaster"] is None winner_name = get_game_winner(self.db_conn, game_id) # If there is no current winner, return an error if not winner_name: raise APIError( 409, log_message="The game currently has no winner." ) # Otherwise, clean up everything else: # We want the gamemaster to FIRST to do the cleanup if is_gamemaster: # Record the win for the player who won winner = get_player(self.db_conn, winner_name) winner["games_won"] += [game_id] game["winner"] = winner_name # Remove all players from game for pname in game["players"]: p = get_player(self.db_conn, pname) p["balls"] = [] p["orig_balls"] = [] # Clear the game's "current" attributes game["players"] = [] game["gamemaster"] = None # Remove the gamemaster's current_game_id player["current_game_id"] = None # Then the remainder of the players can truly leave the game # so they have a chance to query who the winner was from GameState # before having their current_game_id set to None elif gamemaster_has_closed: player["current_game_id"] = None return "Game {} was completed; {} was the winner.".format( game_id, winner_name )
def post(self): """ POST the required parameters to register the pocketing/unpocketing of a ball * `ball`: The ball that was pocketed/unpocketed """ gamemaster = self.get_current_user() ball = self.body['ball'] game_id = get_player(self.db_conn, gamemaster)["current_game_id"] game = Game(self.db_conn, "game_id", game_id) res = {"game_id": game_id} # Authenticate api_assert( game["gamemaster"] == gamemaster, 401, log_message="You are not the gamemaster of the current game") # If ball is already sunk, retable it if ball not in get_balls_on_table(self.db_conn, game_id): game = Game(self.db_conn, "game_id", game_id) # Look for ball in game's set of unclaimed balls if ball in game["orig_unclaimed_balls"]: game["unclaimed_balls"] += [ball] # If it's not there, look for it from each player's set else: for pname in game["players"]: p = Player(self.db_conn, "name", pname) if ball in p["orig_balls"]: p["balls"] += [ball] break res['message'] = "Ball {} was retabled.".format(ball) return res # Otherwise, sink the ball # Look for it in players' first for pname in Game(self.db_conn, "game_id", game_id)["players"]: p = get_player(self.db_conn, pname) if ball in p["balls"]: p["balls"] = list(set(p["balls"]) - {ball}) break else: # Remove ball from unclaimed g = Game(self.db_conn, "game_id", game_id) uballs = list(game["unclaimed_balls"]) uballs.remove(ball) game["unclaimed_balls"] = uballs res["message"] = "Ball {} was sunk.".format(ball) return res
def delete(self): """ DELETE to end the game; this endpoint should only be triggered if GameState indicates there is a winner. """ player_name = self.get_current_user() player = get_player(self.db_conn, player_name) game_id = player["current_game_id"] api_assert(game_id, 400, log_message="You are not currently in" " a game.") game = get_game(self.db_conn, game_id) with DBLock(): is_gamemaster = game["gamemaster"] == player_name gamemaster_has_closed = game["gamemaster"] is None winner_name = get_game_winner(self.db_conn, game_id) # If there is no current winner, return an error if not winner_name: raise APIError(409, log_message="The game currently has no winner.") # Otherwise, clean up everything else: # We want the gamemaster to FIRST to do the cleanup if is_gamemaster: # Record the win for the player who won winner = get_player(self.db_conn, winner_name) winner["games_won"] += [game_id] game["winner"] = winner_name # Remove all players from game for pname in game["players"]: p = get_player(self.db_conn, pname) p["balls"] = [] p["orig_balls"] = [] # Clear the game's "current" attributes game["players"] = [] game["gamemaster"] = None # Remove the gamemaster's current_game_id player["current_game_id"] = None # Then the remainder of the players can truly leave the game # so they have a chance to query who the winner was from GameState # before having their current_game_id set to None elif gamemaster_has_closed: player["current_game_id"] = None return "Game {} was completed; {} was the winner.".format( game_id, winner_name)
def post(self): """POST the required credentials to get back a cookie * `username`: Username * `password`: Password """ player_name = self.body["username"] password = self.body["password"] player = get_player(self.db_conn, player_name, err_code=400) # Check if the given password hashed with the player's known # salt matches the stored password password_match = bcrypt.hashpw( str(password), str(player["salt"]) ) == player['password'] if password_match: self.set_secure_cookie( "user", player_name, options.session_timeout_days ) return {"username": player_name} else: raise APIError( 400, log_message="Bad username/password combo" )
def join_room(db, room_name, password, player_name): """Join room ``room_name`` Updates ``current_players`` entry for room ``room_name`` with player ``player_name`` to the database. :raises APIError: If a room with ``room_name`` does not exist; or if the password is incorrect for room ``room_name``, or if player ``player_name`` does not exist """ # Ensure that the following preconditions are met: # * Correct password to room is give if required # * The player is not already in a room room = get_room(db, room_name) api_assert(password == room['password'], 403, log_message="Bad password for room `{}`.".format(room_name)) player = get_player(db, player_name) api_assert( player_name not in room["current_players"], 409, log_message="Player `{}` already in room `{}`".format( player_name, room_name) ) # If they are met, add the player to the room room["current_players"] += [player_name] player["current_room"] = room_name
def get(self): player = get_player(self.db_conn, self.get_current_user()) if player["current_game_id"]: self.redirect("/room/game") elif player["current_room"]: self.redirect("/room/lobby") else: self.redirect("/room/join")
def assert_non_tenant(db, player_name): player = get_player(db, player_name) player_room = player["current_room"] api_assert( not player_room, 409, log_message=( "{} is already in a room: `{}`. Leave current room" " to join a new one.".format( player_name, player_room ) ) )
def get(self): """ GET to retrieve player info """ player_name = self.get_current_user() player = get_player(self.db_conn, player_name) res = dict(player) res.pop("password") # Redact password res.pop("salt") # Redact salt res.pop("id") # Players don't care about this # Make the following strings b/c the schema expects strings for k in ["current_game_id", "current_room"]: if not res[k]: res[k] = "" return res
def assert_non_tenant(db, player_name): """Assert that player ``player_name`` is not already in a room :type player_name: str :raises APIError: If ``player_name`` is already in a room. """ player = get_player(db, player_name) player_room = player["current_room"] api_assert( not player_room, 409, log_message=( "{} is already in a room: `{}`. Leave current room" " to join a new one.".format( player_name, player_room ) ) )
def post(self): """POST the required credentials to get back a cookie * `username`: Username * `password`: Password """ player_name = self.body["username"] password = self.body["password"] player = get_player(self.db_conn, player_name, err_code=400) # Check if the given password hashed with the player's known # salt matches the stored password password_match = bcrypt.hashpw(str(password), str( player["salt"])) == player['password'] if password_match: self.set_secure_cookie("user", player_name, options.session_timeout_days) return {"username": player_name} else: raise APIError(400, log_message="Bad username/password combo")
def join_room(db, room_name, password, player_name): """Join room `room_name` Updates `current_players` entry for room `room_name` with player `player_name` to the database. :raises APIError: If a room with `room_name` does not exist; or if the password is incorrect for room `room_name`, or if player `player_name` does not exist """ room = get_room(db, room_name) api_assert(password == room['password'], 403, log_message="Bad password for room `{}`.".format(room_name)) player = get_player(db, player_name) api_assert( player_name not in room["current_players"], 409, log_message="Player `{}` already in room `{}`".format( player_name, room_name) ) room["current_players"] += [player_name] player["current_room"] = room_name
def post(self): """POST the required parameter to create a new game; only the owner of a room can make this request * `nbpp`: Number of balls per player """ game_id = uuid.uuid4().hex gamemaster = self.get_current_user() player = get_player(self.db_conn, gamemaster) room_name = player["current_room"] room = get_room(self.db_conn, room_name) api_assert(room["owner"] == gamemaster, 403, log_message="You must own a room to create a game.") player_names = room["current_players"] nplayers = len(player_names) nbpp = self.body["nbpp"] # Make sure values make sense api_assert(nplayers <= nbpp * nplayers <= TOTAL_NUM_BALLS, 400, log_message=("Your math seems to be a little off; " "please pick a `number of balls per player` " "such that each player has at least one ball " "and there are enough to go around for " "everyone.") ) # Create set of 15 balls, shuffle, and then match to players balls = generate_balls(TOTAL_NUM_BALLS) shuffle(balls) players = {} for i in xrange(nplayers): _balls = [] for i in xrange(nbpp): _balls.append(balls.pop()) pname = player_names.pop() players[pname] = _balls unclaimed_balls = balls[:] # Create game self.db_conn["games"].insert( { "game_id": game_id, "players": stringify_list(players.keys()), "orig_players": stringify_list(players.keys()), "unclaimed_balls": stringify_list(unclaimed_balls), "orig_unclaimed_balls": stringify_list(unclaimed_balls), "gamemaster": gamemaster, "winner": None } ) # Assign each player their set of balls, set game, leave room for name, balls in players.iteritems(): p = get_player(self.db_conn, name) p["current_game_id"] = game_id p["balls"] = balls p["orig_balls"] = balls p["current_room"] = None # The room can be deleted self.db_conn["rooms"].delete(name=room_name) return {"game_id": game_id}
def post(self): """POST the required parameter to create a new game; only the owner of a room can make this request * `nbpp`: Number of balls per player """ game_id = uuid.uuid4().hex gamemaster = self.get_current_user() player = get_player(self.db_conn, gamemaster) room_name = player["current_room"] room = get_room(self.db_conn, room_name) api_assert(room["owner"] == gamemaster, 403, log_message="You must own a room to create a game.") player_names = room["current_players"] nplayers = len(player_names) nbpp = self.body["nbpp"] # Make sure values make sense api_assert(nplayers <= nbpp * nplayers <= TOTAL_NUM_BALLS, 400, log_message=("Your math seems to be a little off; " "please pick a `number of balls per player` " "such that each player has at least one ball " "and there are enough to go around for " "everyone.")) # Create set of 15 balls, shuffle, and then match to players balls = generate_balls(TOTAL_NUM_BALLS) shuffle(balls) players = {} for i in xrange(nplayers): _balls = [] for i in xrange(nbpp): _balls.append(balls.pop()) pname = player_names.pop() players[pname] = _balls unclaimed_balls = balls[:] # Create game self.db_conn["games"].insert({ "game_id": game_id, "players": stringify_list(players.keys()), "orig_players": stringify_list(players.keys()), "unclaimed_balls": stringify_list(unclaimed_balls), "orig_unclaimed_balls": stringify_list(unclaimed_balls), "gamemaster": gamemaster, "winner": None }) # Assign each player their set of balls, set game, leave room for name, balls in players.iteritems(): p = get_player(self.db_conn, name) p["current_game_id"] = game_id p["balls"] = balls p["orig_balls"] = balls p["current_room"] = None # The room can be deleted self.db_conn["rooms"].delete(name=room_name) return {"game_id": game_id}