def handle_solution_decision(info): """Handle when a player accepts or rejects a solution.""" writer = info['name'] accepted = info['accepted'] MapsLock() [name, room] = get_name_and_room(flask.request.sid) # This is true player cannot decide on a solution but somehow clicks a button to decide. # (maybe from a second open window). Clientside should ensure this never happens though... # and make buttons that shouldn't be clicked not clickable if writer in rooms_info[room]['endgame']['review_status'][name]: emit( "server_message", "You have already decided on the correctness of this solution. " + "The button you just clicked had no effect.") return accepted_str = "accepted" if accepted else "rejected" emit("server_message", f"{name} " + accepted_str + f" {writer}'s solution!", room=room) if not accepted: rooms_info[room]['endgame']['review_status'][name][ writer] = REVIEWSTATUS_ASSENTING emit("rejection_assent", { "rejecter": name, "writer": writer }, room=room) else: rooms_info[room]['endgame']['review_status'][name][ writer] = REVIEWSTATUS_ACCEPTED track_decided_solution(room, writer, True)
def handle_rejection_assent(info): """Handle when a player assents to a rejection.""" rejecter = info['rejecter'] assented = info['assented'] MapsLock() [name, room] = get_name_and_room(flask.request.sid) if name not in rooms_info[room]['endgame']['review_status'][rejecter] or \ rooms_info[room]['endgame']['review_status'][rejecter][name] != REVIEWSTATUS_ASSENTING: emit( "server_message", "You have already accepted whether or not your solution is incorrect. " + "The button you just clicked had no effect.") return if assented: emit("server_message", f"{name} has accepted that his/her solution is incorrect.", room=room) rooms_info[room]['endgame']['review_status'][rejecter][ name] = REVIEWSTATUS_REJECTED track_decided_solution(room, name, False) return assert name in rooms_info[room]['endgame']['solutions'] reevaluate_msg = { "writer": name, "solution": rooms_info[room]['endgame']['solutions'][name], "rejecter": rejecter, } del rooms_info[room]['endgame']['review_status'][rejecter][name] emit("reevaluate_solution", reevaluate_msg, room=room)
def handle_no_goal_siding(agreed): """Player agreed or disagreed with the no_goal declaration.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) if name not in rooms_info[room]["endgame"]["siders"]: emit( "server_message", "You have already sided on the No Goal. The button you just clicked had no effect." ) return if not agreed: if rooms_info[room]["endgame"]["challenger"] is None: rooms_info[room]["endgame"]["challenger"] = name rooms_info[room]["endgame"]["writers"].append(name) rooms_info[room]["endgame"]["solution_decisions"][name] = [] else: rooms_info[room]["endgame"]["nonwriters"].append(name) rooms_info[room]["endgame"]["siders"].remove(name) if len(rooms_info[room]["endgame"]["siders"]) == 0: rooms_info[room]["endgame"]["endgame_stage"] = "waiting_for_solutions" if rooms_info[room]["endgame"]["challenger"] is None: emit("end_shake_no_goal", room=room) rooms_info[room]["shake_ongoing"] = False rooms_info[room]["endgame"]["endgame_stage"] = "no_goal_finished" return msg_diff = "" if not agreed else "not " emit("server_message", f"{name} has decided " + msg_diff + "to challenge the No Goal.", room=room) check_if_ready_to_present(room, True)
def handle_cube_click(pos): """Highlight cube if it's clicker's turn and clicker hasn't clicked yet.""" MapsLock() [user, room] = get_name_and_room(flask.request.sid) turn_user = get_current_mover(room) if rooms_info[room]["game_finished"] or rooms_info[room]["challenge"] \ or rooms_info[room]["resources"][pos] == MOVED_CUBE_IDX \ or turn_user != user: return do_not_rehighlight = False if rooms_info[room]['touched_cube'] is not None: if rooms_info[room]['touched_cube'] == pos: do_not_rehighlight = True emit("unhighlight_cube", rooms_info[room]['touched_cube'], room=room) rooms_info[room]['touched_cube'] = None if not do_not_rehighlight: if not rooms_info[room]["goalset"] and len(rooms_info[room]["goal"]) >= 6: emit("server_message", "Max number of cubes on goal set! Please press \"Goal Set!\"", room=room) return else: rooms_info[room]['touched_cube'] = pos emit("highlight_cube", pos, room=room)
def handle_siding(writing): """Player sided.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) # Case where perhaps user had two windows open and sided on both. # Second click to the siding buttons would trigger this if statement. if rooms_info[room]["endgame"]["sider"] == None: emit( "server_message", "You have already sided. The button you just clicked had no effect." ) return if writing: rooms_info[room]["endgame"]["writers"].append(name) rooms_info[room]["endgame"]["solution_decisions"][name] = [] else: rooms_info[room]["endgame"]["nonwriters"].append(name) rooms_info[room]["endgame"]["sider"] = None rooms_info[room]["endgame"]["endgame_stage"] = "waiting_for_solutions" msg_diff = "" if writing else "not " emit("server_message", f"{name} has decided " + msg_diff + "to write a solution!", room=room) check_if_ready_to_present(room, False)
def handle_cube_click(pos): """Highlight cube if it's clicker's turn and clicker hasn't clicked yet.""" MapsLock() [user, room] = get_name_and_room(flask.request.sid) if rooms_info[room]["game_finished"]: return if rooms_info[room]["challenge"]: return # Reject if a cube has already been clicked. You touch it you move it! if rooms_info[room]['touched_cube'] is not None: return if rooms_info[room]["resources"][pos] == MOVED_CUBE_IDX: return rooms_info[room]["started_move"] = True turn_idx = rooms_info[room]['turn'] turn_user = rooms_info[room]['players'][turn_idx] if turn_user == user: if not rooms_info[room]["goalset"] and len( rooms_info[room]["goal"]) >= 6: emit("server_message", "Max number of cubes on goal set! Please press \"Goal Set!\"", room=room) return else: rooms_info[room]['touched_cube'] = pos emit("highlight_cube", pos, room=room)
def create_game(): """Create a new game.""" if 'username' not in flask.session: flask.flash("Please log in before creating a game.") return flask.redirect(flask.url_for('show_index')) name = flask.session['username'] connection = equations.model.get_db() # This is ugly and might break if enough games are played game_nonce = None while game_nonce is None: proposed_nonce = str(uuid.uuid4())[:12] game_nonce_dict = connection.execute( "SELECT nonce FROM games " f"WHERE nonce=\'{proposed_nonce}\'" ).fetchone() if game_nonce_dict is None: game_nonce = proposed_nonce MapsLock() if not can_create_room(name): flask.flash("You are already in a game, so you cannot create another.") return flask.redirect(flask.url_for('show_index')) connection.execute( "INSERT INTO games(nonce, ended) " f"VALUES(\'{game_nonce}\', 0);" ) return flask.redirect(flask.url_for('show_game', nonce=game_nonce, name=name))
def handle_game_time_up(): """Thirty five minute mark has been reached.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) if not rooms_info[room]["time_up"]: rooms_info[room]["time_up"] = True if rooms_info[room][ "shake_ongoing"] and rooms_info[room]["endgame"] is None: handle_force_out(room)
def handle_five_minute_warning(): """Thirty minute mark has been reached.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) if not rooms_info[room]["five_minute_warning_called"]: rooms_info[room]["five_minute_warning_called"] = True emit("five_minute_warning_message", room=room) if not rooms_info[room]["shake_ongoing"]: clean_up_finished_room(room)
def handle_bonus_click(): """Bonus button was clicked.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) if get_current_mover(room) != name: print("non mover somehow clicked the bonus button. hacker") return assert not rooms_info[room]["bonus_clicked"] rooms_info[room]["bonus_clicked"] = True rooms_info[room]["started_move"] = True
def handle_set_goal(): """Handle goal set.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) assert name == get_current_mover(room) if rooms_info[room]["game_finished"]: return rooms_info[room]["goalset"] = True next_turn(room)
def receive_message(message_info): """Receive a chat message from client.""" name = message_info['name'] message = message_info['message'] print(f"{name} send the message: {message}") MapsLock() [stored_name, room] = get_name_and_room(flask.request.sid) assert name == stored_name # Send the message to everyone in the room emit('message', message_info, room=room)
def handle_solution_submit(solution): """A player submitted a solution.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) if name not in rooms_info[room]["endgame"]["solutions"]: rooms_info[room]["endgame"]["solutions"][name] = solution check_if_ready_to_present( room, rooms_info[room]["endgame"]["challenge"] == "no_goal") else: emit( "server_message", "You have already submitted a solution. " + "The solution you just submitted was not counted. ")
def register_client(player_info): """Register a client.""" socketid = flask.request.sid name = player_info['name'] room = player_info['room'] # Guaranteed by index.py's join_game and create_game # Will fail when I try to do my old debugging trick, where I hard refresh # on a gamepage after restarting the server. assert name in user_info assert room in rooms_info print(f"Socket {socketid} associated with {name} wants to join room {room}") MapsLock() assert socketid not in socket_info socket_info[socketid] = { "name": name, "room": room, } join_room(room) rooms_info[room]["sockets"].append(socketid) user_info[name]["latest_socketids"][room].append(socketid) mode = user_info[name]["room_modes"][room] rejoin = (mode == equations.data.REJOINED_MODE) joined_as_player = rejoin or (mode == equations.data.PLAYER_MODE) rejoin_str = "rejoined" if rejoin else "joined" spectator_str = "" if joined_as_player else " as spectator" emit("server_message", f"{name} has {rejoin_str}{spectator_str}.", room=room) emit("new_player", rooms_info[room]["players"], room=room) if len(rooms_info[room]["spectators"]) > 0: people_message = "People in this room: " people = ", ".join(list(set([socket_info[x]['name'] for x in rooms_info[room]["sockets"]]))) emit("server_message", people_message + people, room=room) if not joined_as_player and rooms_info[room]['game_started']: # Render every visual aspect of the board correctly for a spectator. # Required only if game has started assert mode == equations.data.SPECTATOR_MODE emit("render_spectator_state", rooms_info[room]) if joined_as_player: # Joined as player. Clientside should render visuals as well as register # callbacks as appropriate (according to whether game has started) emit("render_player_state", rooms_info[room]) if rooms_info[room]["game_finished"]: emit("server_message", "This game has finished.", room=room)
def join_game(): """Join an existing game.""" if 'username' not in flask.session: flask.flash("Please log in before creating a game.") return flask.redirect(flask.url_for('show_index')) name = flask.session['username'] room_id = flask.request.form['room'] MapsLock() get_game_from_db(room_id) return flask.redirect(flask.url_for('show_game', nonce=room_id, name=name))
def handle_bonus_click(): """Bonus button was clicked.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) if get_current_mover(room) != name: print("non mover somehow clicked the bonus button. hacker") return if rooms_info[room]["started_move"]: print("started move but somehow clicked bonus button") return rooms_info[room]["bonus_clicked"] = not rooms_info[room]["bonus_clicked"] print("bonus clicked set to ", rooms_info[room]["bonus_clicked"])
def update_cube_orientation(info): """Update a goalline cube's orientation.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) i = info['order'] new_orientation = info['orientation'] rooms_info[room]["goal"][i]["orientation"] = new_orientation update_msg = { "type": "orientation", "order": i, "new_val": new_orientation, } emit("update_goalline", update_msg, room=room, include_self=False)
def update_cube_xpos(info): """Update a goalline cube's x position.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) i = info['order'] new_x_pos = info['x_pos_per_mille'] rooms_info[room]["goal"][i]["x"] = new_x_pos update_msg = { "type": "x_pos", "order": i, "new_val": new_x_pos, } emit("update_goalline", update_msg, room=room, include_self=False)
def handle_set_goal(): """Handle goal set.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) assert name == get_current_mover(room) if len(rooms_info[room]['goal']) == 0: emit("server_message", "You must set at least one cube on the goal!") return if rooms_info[room]["game_finished"]: return emit("hide_goal_setting_buttons", room=room) rooms_info[room]["goalset"] = True next_turn(room)
def show_game(nonce): """Show the game with nonce nonce.""" if 'username' not in flask.session: flask.flash("Please log in before joining a game.") return flask.redirect(flask.url_for('show_index')) name = flask.session['username'] room = nonce MapsLock() if not (room in rooms_info and name in user_info and room in user_info[name]['room_modes']): if room not in rooms_info: if not get_game_from_db(room): flask.flash( f"The Room you tried to visit (ID of {room}) does not exist!" ) return flask.redirect(flask.url_for('show_index')) if room not in rooms_info or not rooms_info[room]['game_started']: flask.flash( f"The Room you tried to visit (ID of {room}) has not started its game yet. " "Please join by clicking \"Join Existing Game\" below and specifying whether " "you would like to join as a player or a spectator.") return flask.redirect(flask.url_for('show_index')) initialize_user_info_elt(name, room) if name in rooms_info[room][ 'players'] and not rooms_info[room]['game_finished']: user_info[name]["gamerooms"].add(room) user_info[name]["room_modes"][room] = equations.data.REJOINED_MODE else: rooms_info[room]["spectators"].append(name) user_info[name]["room_modes"][room] = equations.data.SPECTATOR_MODE context = { "nonce": nonce, "name": flask.session['username'], } return flask.render_template("game.html", **context)
def show_index(): """Show homepage.""" context = { "logged_in": False, "username": '', "gamerooms": [], } if "username" in flask.session: context['logged_in'] = True context['username'] = flask.session['username'] MapsLock() if context['username'] in user_info: gamerooms = user_info[context['username']]["gamerooms"] for gameroom in gamerooms: if gameroom in rooms_info and rooms_info[gameroom]["game_started"] \ and not rooms_info[gameroom]["game_finished"]: context["gamerooms"].append(gameroom) return flask.render_template("index.html", **context)
def show_index(): """Show homepage.""" context = { "logged_in": False, "username": '', "room_id": None, } if "username" in flask.session: context['logged_in'] = True context['username'] = flask.session['username'] MapsLock() if context['username'] in user_info: room_id = user_info[context['username']]["gameroom"] if room_id is not None and room_id in rooms_info \ and rooms_info[room_id]["game_started"] and \ not rooms_info[room_id]["game_finished"]: context["room_id"] = room_id return flask.render_template("index.html", **context)
def show_game(nonce): """Show the game with nonce nonce.""" if not flask.request.referrer: flask.flash("Please join a game by clicking \"Join Existing Game\"") return flask.redirect(flask.url_for('show_index')) # When a spectator spectates a finished game and the last socket connection # disconnects, on_disconnect in connections.py deletes the room from the room_info, # and then a refresh bypasses join_game in index.py and goes directly to show_game # See Issue #18 on GitHub MapsLock() if nonce not in rooms_info: get_game_from_db(nonce) base_url = equations.app.config["BASE_URL"] context = { "nonce": nonce, "name": flask.session['username'], "shareable_url": base_url + "/game/" + nonce, } return flask.render_template("game.html", **context)
def handle_sector_click(sectorid): """Receive a click action on a playable area of the board.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) print(f"{name} clicked {sectorid} in room {room}") if rooms_info[room]["game_finished"] or rooms_info[room]["touched_cube"] is None: return if name != get_current_mover(room): print(f"Not {name}'s turn. Do nothing.") return if rooms_info[room]["bonus_clicked"]: if sectorid != "forbidden-sector": emit("server_message", "To bonus you must first place a cube in forbidden!", room=room) return else: move_cube(room, sectorid) rooms_info[room]["bonus_clicked"] = False return if not rooms_info[room]["goalset"]: if sectorid != "goal-sector": print(f"Goalsetter clicked on a non-goal area.") return elif sectorid == "goal-sector": print(f"Someone clicked on goal area but goal is already set") return move_cube(room, sectorid) if rooms_info[room]["goalset"]: next_turn(room)
def register_player(player_info): """Register a player.""" socketid = flask.request.sid name = player_info['name'] room = player_info['room'] print( f"Socket {socketid} associated with {name} wants to join room {room}") MapsLock() assert socketid not in socket_info socket_info[socketid] = { "name": name, "room": room, } # This if-else block will be responsible for the rooms_info data structure # as well asserting the "gameroom" field of users in user_info joined_as_player = True rejoin = False if room not in rooms_info: # for create room # This is a new game room and players can always join as player # See Issue #17 on Github if name in user_info: # this assertion should be guaranteed by call to # can_create_room() from create_game assert user_info[name]["gameroom"] is None rooms_info[room] = { "game_started": False, "game_finished": False, "players": [name], "spectators": [], "sockets": [socketid] } print(f"{name} joined room {room} as new player") else: # for join room # If the user is a player in this room, he/she will join as player # Else, the user will join as spectator if the game has started or if # there are no spots left or if the player is in another match # as a player (regardless of whether that match has started or not) # Otherwise, the user will join as player # See Issue #17 on Github if name in rooms_info[room]["players"]: # join as existing player # assert name in user_info game coulda ended # assert user_info[name]["gameroom"] == room game coulda ended rejoin = True print(f"{name} rejoined room {room}") elif rooms_info[room]["game_started"] or len(rooms_info[room]["players"]) >= 3 \ or (name in user_info and user_info[name]["gameroom"] != room): # join as spectator rooms_info[room]["spectators"].append(name) joined_as_player = False print(f"{name} joined room {room} as spectator") else: # join as new player if name in user_info: assert user_info[name]["gameroom"] is None rooms_info[room]["players"].append(name) print(f"{name} joined room {room} as new player") rooms_info[room]["sockets"].append(socketid) join_room(room) if name not in user_info: user_info[name] = { "latest_socketids": {}, "gameroom": room if joined_as_player else None, } user_info[name]["latest_socketids"][room] = socketid else: if joined_as_player: user_info[name]['gameroom'] = room user_info[name]["latest_socketids"][room] = socketid if rooms_info[room]['game_started'] and not joined_as_player: # Render every visual aspect of the board correctly for a spectator. # Required only if game has started emit("render_spectator_state", rooms_info[room]) if joined_as_player: # Joined as player. Clientside should render visuals as well as register # callbacks as appropriate (according to whether game has started) emit("render_player_state", rooms_info[room]) rejoin_str = "rejoined" if rejoin else "joined" spectator_str = "" if joined_as_player else " as spectator" emit("server_message", f"{name} has {rejoin_str}{spectator_str}.", room=room) if len(rooms_info[room]["spectators"]) > 0: people_message = "People in this room: " people = ", ".join( [socket_info[x]['name'] for x in rooms_info[room]["sockets"]]) emit("server_message", people_message + people, room=room) if rooms_info[room]["game_finished"]: emit("server_message", "This game has finished.", room=room)
def on_disconnect(): """Handle disconnect.""" print(f"Client {flask.request.sid} disconnected!") socketid = flask.request.sid MapsLock() if socketid not in socket_info: # Note: MapsLock makes register_player atomic, so we can safely say # in this case socket disconnected before it *started*. print("Socket disconnected before register_player started") return [username, room] = get_name_and_room(socketid) # Leave the room leave_room(room) # Update socket_info del socket_info[socketid] # Update room info filter(lambda x: x != username, rooms_info[room]["spectators"]) rooms_info[room]["sockets"].remove(socketid) if len(rooms_info[room]["sockets"]) == 0: # If all players leave, then game is considered finished even if it's not. if rooms_info[room][ "game_started"] and not rooms_info[room]["game_finished"]: rooms_info[room]["game_finished"] = True db_insert(room, rooms_info[room]) for player in rooms_info[room]["players"]: assert player in user_info user_info[player]["gameroom"] = None del rooms_info[room] # Update user_info if room in user_info[username]["latest_socketids"] and \ user_info[username]["latest_socketids"][room] == socketid: print(f"{username} is no longer connected to {room}") del user_info[username]["latest_socketids"][room] # If a player leaves before a game is started, then remove him/her as a player current_game = user_info[username]["gameroom"] if current_game is not None: if current_game not in rooms_info: user_info[username]["gameroom"] = None elif not rooms_info[current_game]["game_started"]: user_info[username]["gameroom"] = None rooms_info[current_game]["players"].remove(username) # Unmap user if he/she is not in any rooms and he/she is not playing a game if len(user_info[username]["latest_socketids"]) == 0 and \ user_info[username]["gameroom"] is None: del user_info[username] emit("server_message", f"{username} has left.", room=room) print(f"Client {socketid}: {username} left room {room}") else: # User made a new connection to the room, so current socket is outdated print(f"Outdated socket {socketid} for user {username} disconnected")
def on_disconnect(): """Handle disconnect.""" print(f"Client {flask.request.sid} disconnected!") socketid = flask.request.sid MapsLock() if socketid not in socket_info: # Note: MapsLock makes register_player atomic, so we can safely say # in this case socket disconnected before register_player *started*. print("Socket disconnected before register_player started") return [username, room] = get_name_and_room(socketid) # Leave the room leave_room(room) # Update socket_info del socket_info[socketid] # Update room info if username in rooms_info[room]['spectators']: # Remove spectators on disconnect. But do not remove player until game finishes. rooms_info[room]['spectators'].remove(username) rooms_info[room]["sockets"].remove(socketid) if len(rooms_info[room]["sockets"]) == 0: # If all players and spectators leave, then game is considered finished even if it's not. # TODO Right now this is only way a game ends. When implement timing, may need to reconsider this. if not rooms_info[room]["game_finished"]: rooms_info[room]["game_finished"] = True db_insert(room, rooms_info[room]) for player in rooms_info[room]["players"]: assert player in user_info assert room in user_info[player]["gamerooms"] user_info[player]["gamerooms"].remove(room) del rooms_info[room] # Update user_info # On a refresh on a game page, due to delay in socket disconnect, new socket # (most likely) has connected by the time this code runs and (potentially) deletes # the user from the user_info dict. Even if not, worst thing that can happen # is that the game is marked as finished. assert room in user_info[username]["latest_socketids"].keys() user_info[username]["latest_socketids"][room].remove(socketid) if len(user_info[username]["latest_socketids"][room]) == 0: print(f"{username} is no longer connected to {room}") del user_info[username]["latest_socketids"][room] assert room in user_info[username]["room_modes"].keys() del user_info[username]["room_modes"][room] # If a player leaves before a game is started, then remove him/her as a player if room in rooms_info and not rooms_info[room]["game_started"] \ and username in rooms_info[room]["players"]: if room in user_info[username]["gamerooms"]: # Need if statement because could gotten removed above in update rooms_info user_info[username]["gamerooms"].remove(room) if room in rooms_info: rooms_info[room]["players"].remove(username) emit("player_left", rooms_info[room]["players"], room=room) if room in user_info[username]["gamerooms"] and room not in rooms_info: user_info[username]["gamerooms"].remove(room) # Unmap user if he/she is not in any rooms and he/she is not playing a game if len(user_info[username]["latest_socketids"]) == 0 and \ len(user_info[username]["gamerooms"]) == 0: del user_info[username] emit("server_message", f"{username} has left.", room=room) print(f"Client {socketid}: {username} left room {room}") else: print(f"Socket {socketid} for user {username} disconnected, but user " "still has another connection to the room open")
def start_shake(new_game, is_restart): """Handle logic for starting a shake. new_game specifies if shake is the first shake in a game.""" MapsLock() [name, room] = get_name_and_room(flask.request.sid) print(f"{name} pressed start_game for room {room}!") if rooms_info[room]["game_finished"]: return if not new_game and rooms_info[room]["five_minute_warning_called"]: emit("server_message", "Five minute warning has been called so a new shake cannot be started.", room=room) return if room not in rooms_info or (new_game and rooms_info[room]['game_started']) \ or (not new_game and rooms_info[room]['shake_ongoing']): print("Game start rejected") return current_players = rooms_info[room]['players'] if len(current_players) < 2: emit('server_message', f"{name} clicked \"Start Game\" but a game can only be started with 2 or 3 players.", room=room) return assert name in current_players assert len(current_players) <= 3 random.seed(time.time()) rolled_cubes = [random.randint(0, 5) for _ in range(24)] if is_restart: # Goalsetter needs to be the same as the previous shake. rooms_info[room]['goalsetter_index'] = \ (rooms_info[room]['goalsetter_index'] - 1) % len(rooms_info[room]['players']) if new_game: rooms_info[room] = { "game_started": True, "game_finished": False, "players": current_players, "spectators": rooms_info[room]["spectators"], "sockets": rooms_info[room]["sockets"], "p1scores": [0], "p2scores": [0], "p3scores": [0], "starttime": time.time(), # cube_index is poorly named. fixed length of 24, index is cube's id. # first six are red, next six are blue, next six are green, last six are black. # so if the first element of cube_index was x (where 0 <= x <= 5), the # corresponding cube is given by the file "rx.png" (where x is replaced w/#) "cube_index": rolled_cubes[:], "resources": rolled_cubes, # fixed length of 24 "goal": [], # stores cube id, x pos on canvas, orientation of cube "required": [], # stores cube ids (based on cube_index); same for 2 below "permitted": [], "forbidden": [], "turn": random.randint(0, len(current_players) - 1), "goalset": False, "num_timer_flips": 0, "10s_warning_called": False, "challenge": None, "touched_cube": None, "bonus_clicked": False, "started_move": False, "endgame": None, "shake_ongoing": True, "five_minute_warning_called": False, "time_up": False, } rooms_info[room]['goalsetter_index'] = rooms_info[room]['turn'] game_begin_instructions = { 'cubes': rolled_cubes, 'players': current_players, 'starter': name, 'goalsetter': get_current_mover(room), 'starttime': rooms_info[room]['starttime'], } emit("begin_game", game_begin_instructions, room=room) else: rooms_info[room]["cube_index"] = rolled_cubes[:] rooms_info[room]["resources"] = rolled_cubes rooms_info[room]["goal"] = [] rooms_info[room]["required"] = [] rooms_info[room]["permitted"] = [] rooms_info[room]["forbidden"] = [] rooms_info[room]['goalsetter_index'] = \ (rooms_info[room]['goalsetter_index'] + 1) % len(rooms_info[room]['players']) rooms_info[room]["turn"] = rooms_info[room]['goalsetter_index'] rooms_info[room]["goalset"] = False rooms_info[room]["num_timer_flips"] = 0 rooms_info[room]["10s_warning_called"] = False rooms_info[room]["challenge"] = None rooms_info[room]["touched_cube"] = None rooms_info[room]["bonus_clicked"] = False rooms_info[room]["started_move"] = False rooms_info[room]["endgame"] = None rooms_info[room]["shake_ongoing"] = True goalsetter = get_current_mover(room) shake_begin_instructions = { 'cubes': rolled_cubes, 'players': current_players, 'goalsetter': goalsetter, 'show_bonus': not is_leading(room, goalsetter), } emit("begin_shake", shake_begin_instructions, room=room)
def handle_challenge(socketid, challenge): """Handle a challenge.""" MapsLock() [name, room] = get_name_and_room(socketid) print(f"{name} pressed {challenge}") assert room in rooms_info if rooms_info[room][ "challenge"] is not None and challenge != "p_flub": # TODO can't p flub after 1st minute. TEST!! print(f"{name} tried to challenge {challenge} but was too late") return defender = get_previous_mover(room) challenge_message = f"{name} has called {challenge_translation[challenge]}" if challenge == "no_goal": challenge_message += "!" else: if not rooms_info[room]['goalset']: emit("server_message", "You cannot challenge; the goal has not been set!") return challenge_message += f" on {defender}!" sider = None if rooms_info[room]["started_move"]: # TODO minus 1? challenge_message += f" But {name} has started their move so {name} " \ f"must finish the move and cannot challenge!" emit("server_message", challenge_message, room=room) return elif defender == name: # TODO minus 1? challenge_message += f" But {name} just moved so {name} cannot challenge!" emit("server_message", challenge_message, room=room) return elif challenge != "no_goal" and len(rooms_info[room]['players']) == 3: sider_list = list( filter(lambda x: x != name and x != defender, rooms_info[room]['players'])) assert len(sider_list) == 1 sider = sider_list[0] challenge_message += f" {sider} has two minutes to side!" if challenge == "no_goal": if rooms_info[room]["goalset"]: no_goal_msg = f"{name} has called Challenge No Goal! But Goal has already been set, so challenge cannot be made!" # TODO minus one? emit("server_message", no_goal_msg, room=room) return else: assert defender is None if name != get_current_mover(room): no_goal_err_msg = f"{name} called Challenge No Goal but that is " \ f"not allowed because {name} is not setting the goal!" emit("server_message", no_goal_err_msg, room=room) return else: if not rooms_info[room]["goalset"]: assert defender is None emit("server_message", "Goal has not been set yet! You cannot challenge.", room=room) # TODO handle this return assert defender is not None if challenge == "a_flub": cubes_left = len([x for x in rooms_info[room]["resources"] if x != -1]) # TODO magic bad if cubes_left < 2: emit("server_message", f"{name} called Challenge Now but that is not allowed!", room=room) # TODO minus 1 return if challenge == "p_flub": pass # TODO Gotta make sure not called after first minute in a force out rooms_info[room]["challenge"] = challenge initialize_endgame(room, challenge, name, defender, sider) emit("server_message", challenge_message, room=room) challenge_info = { "challenge": challenge, "defender": defender, "caller": name, "sider": sider, "writers": rooms_info[room]["endgame"]["writers"], "nonwriters": rooms_info[room]["endgame"]["nonwriters"], } emit("handle_challenge", challenge_info, room=room)
def handle_game_over(): """The game is finished now.""" MapsLock() [_, room] = get_name_and_room(flask.request.sid) clean_up_finished_room(room)