Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
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)
Пример #4
0
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)
Пример #5
0
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)
Пример #6
0
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)
Пример #7
0
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))
Пример #8
0
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)
Пример #9
0
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)
Пример #10
0
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
Пример #11
0
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)
Пример #12
0
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)
Пример #13
0
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. ")
Пример #14
0
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)
Пример #15
0
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))
Пример #16
0
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"])
Пример #17
0
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)
Пример #18
0
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)
Пример #19
0
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)
Пример #20
0
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)
Пример #21
0
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)
Пример #22
0
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)
Пример #23
0
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)
Пример #24
0
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)
Пример #25
0
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)
Пример #26
0
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")
Пример #27
0
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")
Пример #28
0
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)
Пример #29
0
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)
Пример #30
0
def handle_game_over():
    """The game is finished now."""
    MapsLock()
    [_, room] = get_name_and_room(flask.request.sid)
    clean_up_finished_room(room)