Example #1
0
def parse_draft_status(blob):
    # TODO: need to implement the sorting algo shown here:
    # TODO: https://github.com/Fugiman/deckmaster/blob/559e3b94bb105387a0e33463e4b5f718ab91721d/client/updater.go#L113
    """ return a.RarityRank() > b.RarityRank() ||
            (a.RarityRank() == b.RarityRank() && a.ColorRank() > b.ColorRank()) ||
            (a.RarityRank() == b.RarityRank() && a.ColorRank() == b.ColorRank() && a.CMC < b.CMC) ||
            (a.RarityRank() == b.RarityRank() && a.ColorRank() == b.ColorRank() && a.CMC == b.CMC && a.Name < b.Name)"""
    import app.mtga_app as mtga_app
    collection_count = []
    picked_cards_this_draft = []
    if "pickedCards" in blob and blob["pickedCards"]:
        picked_cards_this_draft = blob["pickedCards"]
    for card in blob["draftPack"]:
        card_obj = util.all_mtga_cards.find_one(card).to_serializable()
        if card in mtga_app.mtga_watch_app.collection:
            card_obj["count"] = min(
                mtga_app.mtga_watch_app.collection[card] +
                picked_cards_this_draft.count(card), 4)
        else:
            card_obj["count"] = min(0 + picked_cards_this_draft.count(card), 4)
        collection_count.append(card_obj)
    collection_count.sort(key=lambda x: (-1 * util.rank_rarity(x[
        "rarity"]), util.rank_colors(x[
            "color_identity"]), util.rank_cost(x["cost"]), x["pretty_name"]))
    general_output_queue.put({"draft_collection_count": collection_count})
Example #2
0
 def send_error(self, error):
     self.error_count += 1
     general_output_queue.put({
         "error": "MTGAWatchError",
         "msg": error,
         "count": self.error_count
     })
Example #3
0
def parse_mulligan_req_message(message, timestamp=None):
    import app.mtga_app as mtga_app
    number_cards = message["prompt"]["parameters"][0]["numberValue"]
    player_seat_id = message["systemSeatIds"][0]
    player = mtga_app.mtga_watch_app.game.get_player_in_seat(player_seat_id)
    mtga_app.mtga_logger.info("MULL: {}".format(player.hand.cards))
    with mtga_app.mtga_watch_app.game_lock:  # the game state may become inconsistent in between these steps, so lock it
        if number_cards < 6:
            number_mulligans = 6 - number_cards
            starting_hand_size = 7 - number_mulligans
            player.mulligan_count = number_mulligans
            mulligan_text = [" mulligans to ", str(starting_hand_size), ": "]
        else:  # starting hand
            mulligan_text = ["'s starting hand: "]
        player_texts = build_event_texts_from_iid_or_grpid(
            player_seat_id, mtga_app.mtga_watch_app.game)
        card_texts = []
        for card in player.hand.cards:
            card_texts.append(*build_event_texts_from_iid_or_grpid(
                card.game_id, mtga_app.mtga_watch_app.game))
        event_texts = [*player_texts, *mulligan_text]
        for card_text in card_texts:
            event_texts.extend([card_text, ", "])
        if event_texts[-1] == ", ":
            event_texts.pop()
        queue_obj = {"game_history_event": event_texts}
        mtga_app.mtga_watch_app.game.events.append(
            queue_obj["game_history_event"])
        general_output_queue.put(queue_obj)
def click_event(_x, _y, button, pressed):
    if pressed:
        if button == mouse.Button.right:
            general_output_queue.put({"right_click": True})
        if button == mouse.Button.left:
            general_output_queue.put({"left_click": True})
    if not all_die_queue.empty():
        return False
Example #5
0
def parse_draft_status(blob):
    # TODO: need to implement the sorting algo shown here:
    # TODO: https://github.com/Fugiman/deckmaster/blob/559e3b94bb105387a0e33463e4b5f718ab91721d/client/updater.go#L113

    """ return a.RarityRank() > b.RarityRank() ||
            (a.RarityRank() == b.RarityRank() && a.ColorRank() > b.ColorRank()) ||
            (a.RarityRank() == b.RarityRank() && a.ColorRank() == b.ColorRank() && a.CMC < b.CMC) ||
            (a.RarityRank() == b.RarityRank() && a.ColorRank() == b.ColorRank() && a.CMC == b.CMC && a.Name < b.Name)"""
    import app.mtga_app as mtga_app

    collection_count = []
    picked_cards_this_draft = []
    if "pickedCards" in blob and blob["pickedCards"]:
        picked_cards_this_draft = blob["pickedCards"]

    if blob["draftPack"]:
        for card in blob["draftPack"]:
            card_obj = util.all_mtga_cards.find_one(card).to_serializable()
            if card in mtga_app.mtga_watch_app.collection:
                card_obj["count"] = min(mtga_app.mtga_watch_app.collection[card] + picked_cards_this_draft.count(card), 4)
            else:
                card_obj["count"] = min(0 + picked_cards_this_draft.count(card), 4)
            collection_count.append(card_obj)
        collection_count.sort(key=lambda x: (-1 * util.rank_rarity(x["rarity"]), util.rank_colors(x["color_identity"]), util.rank_cost(x["cost"]), x["pretty_name"]))
        general_output_queue.put({"draft_collection_count": collection_count})
    else:
        blob["draftPack"] = []

    draftId = blob["draftId"]
    picks = picked_cards_this_draft[:]
    pack = blob['draftPack'][:]

    draft_history = mtga_app.mtga_watch_app.draft_history
    if draft_history.get(draftId, None):
        report = {}
        # report['picks'] = [int(grpid) for grpid in draft_history[draftId]['picks'] ]
        report['draftID'] = draftId
        report['playerID'] = blob["playerId"]
        report['pickNumber'] = draft_history[draftId]['picknum']
        report['packNumber'] = draft_history[draftId]['packnum']
        report['pack'] = [int(grpid) for grpid in draft_history[draftId]['pack']]

        old = draft_history[draftId]['picks'][:]
        new = picks[:]
        for c in old:
            new.remove(c)
        report['pick'] = int(new[0])

        # send report to inspector
        app.mtga_app.mtga_logger.info("{}{}".format(util.ld(), report))
        pass_through("draftPick", report["playerID"], report)
    if pack:
        draft_history[draftId] = {'picks': picks, 'pack': pack, 'picknum': blob["pickNumber"], 'packnum': blob["packNumber"]}
    else:
        draft_history[draftId] = None
Example #6
0
 def check_for_client_id(blob):
     if "authenticateResponse" in blob:
         if "clientId" in blob["authenticateResponse"]:
             # screw it, no one else is going to use this message, mess up the timestamp, who cares
             with mtga_watch_app.game_lock:
                 if mtga_watch_app.player_id != blob[
                         "authenticateResponse"]['clientId']:
                     mtga_watch_app.player_id = blob[
                         "authenticateResponse"]['clientId']
                     mtga_logger.debug(
                         "{}check_for_client_id: got new clientId".format(
                             util.ld()))
                     mtga_watch_app.save_settings()
             general_output_queue.put(
                 {"authenticateResponse": blob["authenticateResponse"]})
Example #7
0
def parse_game_results(_unused_locked, match_id, result_list):
    import app.mtga_app as mtga_app
    for idx, result in enumerate(result_list):
        if not app.mtga_app.mtga_watch_app.match.has_results(idx):
            # scope = result["scope"]
            # if scope == 'MatchScope_Match':  # TODO: with BO3, check games too. (might be in a different event type)
            winning_team = result["winningTeamId"]

            mtga_app.mtga_watch_app.game.final = True
            mtga_app.mtga_watch_app.game.winner = mtga_app.mtga_watch_app.game.get_player_in_seat(
                winning_team)
            # let electron handle the upload
            result = {
                "match_complete": True,
                "game": mtga_app.mtga_watch_app.game.to_json()
            }

            if "end" not in mtga_app.mtga_watch_app.game.recorded_targetspecs:
                mtga_app.mtga_watch_app.game.recorded_targetspecs.append("end")
                reason = None
                if "reason" in result.keys():
                    reason = result["reason"].split("_")[1]

                won_text = "{} won!".format(
                    mtga_app.mtga_watch_app.game.winner.player_name)
                if reason:
                    won_text += "({})".format(reason)

                event_text = build_event_text(won_text, "game")

                event_texts = [event_text]
                queue_obj = {"game_history_event": event_texts}
                mtga_app.mtga_watch_app.game.events.append(
                    queue_obj["game_history_event"])
                general_output_queue.put(queue_obj)

            app.mtga_app.mtga_watch_app.match.add_result(result)
            game_state_change_queue.put(result)
            if match_id != mtga_app.mtga_watch_app.game.match_id:
                fstr = "match_id {} ended, but doesn't match current game object ({})!"
                raise Exception(
                    fstr.format(match_id,
                                mtga_app.mtga_watch_app.game.match_id))
Example #8
0
 def send_message(self, message):
     general_output_queue.put({"color": "black", "msg": message})
Example #9
0
def parse_match_playing(blob):
    # MatchGameRoomStateType_Playing
    import app.mtga_app as mtga_app
    temp_players = {1: {}, 2: {}}
    game_room_info = blob["matchGameRoomStateChangedEvent"]["gameRoomInfo"]
    event_id = game_room_info['gameRoomConfig']['eventId']
    game_room_players = game_room_info["players"]

    for player in game_room_players:
        temp_players[player["systemSeatId"]]["player_id"] = player["userId"]

    game_room_config = game_room_info["gameRoomConfig"]

    reserved_players = game_room_config["reservedPlayers"]
    for player in reserved_players:
        temp_players[player["systemSeatId"]]["name"] = player["playerName"]

    match_config = game_room_config["matchConfig"]
    if "teams" in match_config:
        teams = match_config["teams"]
        for team in teams:
            players = team["players"]
            for player in players:
                player_seat = player["systemSeatId"]
                temp_players[player_seat]["deck"] = util.card_ids_to_card_list(
                    player["deckCards"])
    for player_idx in [1, 2]:
        if "deck" not in temp_players[player_idx]:
            temp_players[player_idx]["deck"] = []
    # set up shared zones
    shared_battlefield = Zone("battlefield")
    shared_exile = Zone("exile")
    shared_limbo = Zone("limbo")
    shared_stack = Zone("stack")
    player1 = Player(temp_players[1]["name"], temp_players[1]["player_id"], 1,
                     shared_battlefield, shared_exile, shared_limbo,
                     shared_stack, temp_players[1]["deck"])
    player2 = Player(temp_players[2]["name"], temp_players[2]["player_id"], 2,
                     shared_battlefield, shared_exile, shared_limbo,
                     shared_stack, temp_players[2]["deck"])
    with mtga_app.mtga_watch_app.game_lock:
        if mtga_app.mtga_watch_app.player_id == player1.player_id:
            hero = player1
            opponent = player2
        elif mtga_app.mtga_watch_app.player_id == player2.player_id:
            hero = player2
            opponent = player1
        else:
            raise Exception(
                "Don't know who hero is: player_id: {} / player 1: {} / player 2: {}"
                .format(mtga_app.mtga_watch_app.player_id, player1.player_id,
                        player2.player_id))
        hero.is_hero = True
        if mtga_app.mtga_watch_app.intend_to_join_game_with:
            hero.original_deck = mtga_app.mtga_watch_app.intend_to_join_game_with
        opponent_rank = "Unknown"
        if mtga_app.mtga_watch_app.match.opponent_name == opponent.player_name:
            opponent_rank = mtga_app.mtga_watch_app.match.opponent_rank
        match_id = game_room_info['gameRoomConfig'][
            'matchId'] + "-game1-{}".format(hero.player_id)

        hero_text = build_event_text(hero.player_name, "hero")
        oppo_text = build_event_text(opponent.player_name, "opponent")

        event_texts = [hero_text, " vs ", oppo_text]
        queue_obj = {"game_history_event": event_texts}
        general_output_queue.put(queue_obj)
        mtga_app.mtga_watch_app.game = Game(match_id, hero, opponent,
                                            shared_battlefield, shared_exile,
                                            shared_limbo, shared_stack,
                                            event_id, opponent_rank)
        mtga_app.mtga_watch_app.game.events.append(
            queue_obj["game_history_event"])
Example #10
0
def pass_through(title, player_key, blob):
    general_output_queue.put({title: blob, "player_key": player_key})
Example #11
0
def parse_game_state_message(message, timestamp=None):
    # DOM: ok
    import app.mtga_app as mtga_app
    with mtga_app.mtga_watch_app.game_lock:  # the game state may become inconsistent in between these steps, so lock it
        if "turnInfo" in message.keys():
            if "turnNumber" in message["turnInfo"].keys():
                player = app.mtga_app.mtga_watch_app.game.get_player_in_seat(
                    message["turnInfo"]["activePlayer"])
                if "decisionPlayer" in message["turnInfo"].keys():
                    decisionPlayer = app.mtga_app.mtga_watch_app.game.get_player_in_seat(
                        message["turnInfo"]["decisionPlayer"])
                else:
                    decisionPlayer = app.mtga_app.mtga_watch_app.game.last_decision_player
                if timestamp:
                    now = datetime.datetime.now()
                    if app.mtga_app.mtga_watch_app.game.last_log_timestamp is None:
                        app.mtga_app.mtga_watch_app.game.last_log_timestamp = timestamp
                        app.mtga_app.mtga_watch_app.game.last_measured_timestamp = now
                        app.mtga_app.mtga_watch_app.game.log_start_time = timestamp
                        app.mtga_app.mtga_watch_app.game.last_decision_player = decisionPlayer

                    measured_time_diff = now - app.mtga_app.mtga_watch_app.game.last_measured_timestamp
                    log_time_diff = timestamp - app.mtga_app.mtga_watch_app.game.last_log_timestamp

                    if measured_time_diff > log_time_diff:
                        log_time_diff = measured_time_diff  # some turns are really fast, and the logs see it as 0 seconds. Add what we measured instead,

                    app.mtga_app.mtga_watch_app.game.last_log_timestamp = timestamp
                    app.mtga_app.mtga_watch_app.game.last_measured_timestamp = now
                    ct_obj = {
                        "turnInfo":
                        message["turnInfo"],
                        "diff":
                        log_time_diff,
                        "countsAgainst":
                        app.mtga_app.mtga_watch_app.game.last_decision_player
                    }
                    app.mtga_app.mtga_watch_app.game.chess_timer.append(ct_obj)
                    general_output_queue.put({
                        "decisionPlayerChange":
                        True,
                        "heroIsDeciding":
                        decisionPlayer == app.mtga_app.mtga_watch_app.game.hero
                    })
                    app.mtga_app.mtga_watch_app.game.last_decision_player = decisionPlayer
                app.mtga_app.mtga_watch_app.game.turn_number = message[
                    "turnInfo"]["turnNumber"]
                other_player_seat = 2 if message["turnInfo"][
                    "activePlayer"] == 1 else 1
                other_player = app.mtga_app.mtga_watch_app.game.get_player_in_seat(
                    other_player_seat)
                app.mtga_app.mtga_watch_app.game.current_player = player.player_name
                if not app.mtga_app.mtga_watch_app.game.on_the_play:
                    if message["turnInfo"]["turnNumber"] % 2 == 1:
                        app.mtga_app.mtga_watch_app.game.on_the_play = player.player_name
                    else:
                        app.mtga_app.mtga_watch_app.game.on_the_play = other_player.player_name
                app.mtga_app.mtga_watch_app.game.current_phase = message[
                    "turnInfo"]["phase"]
                turn_tuple = (message["turnInfo"]["turnNumber"], "phase")
                if turn_tuple not in mtga_app.mtga_watch_app.game.recorded_targetspecs:
                    mtga_app.mtga_watch_app.game.recorded_targetspecs.append(
                        turn_tuple)
                    turn = turn_tuple[0]
                    active_player_seat = message["turnInfo"]["activePlayer"]
                    active_player = mtga_app.mtga_watch_app.game.get_player_in_seat(
                        active_player_seat)
                    if turn % 2 == 1:
                        text = "{} / {} Turn {}".format(
                            turn, active_player.player_name, int(
                                (turn + 1) / 2))
                    else:
                        text = "{} / {} Turn {}".format(
                            turn, active_player.player_name, int((turn / 2)))
                    text_obj = build_event_text(text, "turn")
                    queue_obj = {"game_history_event": [text_obj]}
                    mtga_app.mtga_watch_app.game.events.append(
                        queue_obj["game_history_event"])
                    general_output_queue.put(queue_obj)
                if "step" in message["turnInfo"].keys():
                    app.mtga_app.mtga_watch_app.game.current_phase += "-{}".format(
                        message["turnInfo"]["step"])
            app.mtga_app.mtga_logger.debug(message["turnInfo"])
        if 'gameInfo' in message.keys():
            if 'matchState' in message['gameInfo']:
                game_number = message['gameInfo']['gameNumber']
                game_player_id = "-game{}-{}".format(
                    game_number, mtga_app.mtga_watch_app.game.hero.player_id)
                match_id_raw = message['gameInfo']['matchID']
                match_id = message['gameInfo']['matchID'] + game_player_id

                if 'results' in message['gameInfo']:
                    results = message['gameInfo']['results']
                    parse_game_results(True, match_id, results)
                if message['gameInfo']['matchState'] == "MatchState_GameInProgress" and \
                        game_number > max(len(app.mtga_app.mtga_watch_app.match.game_results), 1):
                    shared_battlefield = Zone("battlefield")
                    shared_exile = Zone("exile")
                    shared_limbo = Zone("limbo")
                    shared_stack = Zone("stack")
                    new_hero = Player(
                        mtga_app.mtga_watch_app.game.hero.player_name,
                        mtga_app.mtga_watch_app.game.hero.player_id,
                        mtga_app.mtga_watch_app.game.hero.seat,
                        shared_battlefield, shared_exile, shared_limbo,
                        shared_stack,
                        mtga_app.mtga_watch_app.game.hero._deck_cards)

                    new_oppo = Player(
                        mtga_app.mtga_watch_app.game.opponent.player_name,
                        mtga_app.mtga_watch_app.game.opponent.player_id,
                        mtga_app.mtga_watch_app.game.opponent.seat,
                        shared_battlefield, shared_exile, shared_limbo,
                        shared_stack,
                        mtga_app.mtga_watch_app.game.opponent._deck_cards)
                    new_hero.is_hero = True
                    if mtga_app.mtga_watch_app.intend_to_join_game_with:
                        new_hero.original_deck = mtga_app.mtga_watch_app.intend_to_join_game_with
                        new_match_id = match_id_raw + "-game{}-{}".format(
                            game_number, new_hero.player_id)
                        mtga_app.mtga_watch_app.game = Game(
                            new_match_id, new_hero, new_oppo,
                            shared_battlefield, shared_exile, shared_limbo,
                            shared_stack,
                            app.mtga_app.mtga_watch_app.match.event_id,
                            app.mtga_app.mtga_watch_app.match.opponent_rank)
        if 'annotations' in message.keys():
            for annotation in message['annotations']:
                annotation_type = annotation['type'][0]
                if annotation_type == 'AnnotationType_ObjectIdChanged':
                    try:
                        original_id = None
                        new_id = None
                        details = annotation['details']
                        for detail in details:
                            if detail['key'] == "orig_id":
                                original_id = detail["valueInt32"][0]
                                mtga_app.mtga_watch_app.game.ignored_iids.add(
                                    original_id)
                                # NOTE: at one point Spencer thought it might be correct to ignore these AFTER
                                # parsing the whole gameStateMessage, i.e. put these in a list here, and only add them
                                # to ignored_iid's at the end of this function.
                                #
                                # That was incorrect, and led to cards flip-flopping in the UI.
                                # This is correct as is.
                            elif detail['key'] == "new_id":
                                new_id = detail["valueInt32"][0]
                        card_with_iid = mtga_app.mtga_watch_app.game.find_card_by_iid(
                            original_id)
                        if not card_with_iid:  # no one has ref'd yet, we don't care
                            continue
                        new_card_already_exists = mtga_app.mtga_watch_app.game.find_card_by_iid(
                            new_id)
                        if new_card_already_exists:  # just wipe the old card, the new card is already there
                            assert new_card_already_exists.mtga_id == card_with_iid.mtga_id or -1 in [
                                new_card_already_exists.mtga_id,
                                card_with_iid.mtga_id
                            ], "{} / {}".format(
                                new_card_already_exists.mtga_id,
                                card_with_iid.mtga_id)
                            card_with_iid.mtga_id = -1
                        else:
                            card_with_iid.previous_iids.append(original_id)
                            card_with_iid.game_id = new_id
                    except:
                        app.mtga_app.mtga_logger.error(
                            "{}Exception @ count {}".format(
                                util.ld(True),
                                app.mtga_app.mtga_watch_app.error_count))
                        app.mtga_app.mtga_logger.error(
                            "{}parsers:parse_game_state_message - error parsing annotation:"
                            .format(util.ld(True)))
                        app.mtga_app.mtga_logger.error(
                            pprint.pformat(annotation))
                        app.mtga_app.mtga_watch_app.send_error(
                            "Exception during parse AnnotationType_ObjectIdChanged. Check log for more details"
                        )
                if annotation_type == "AnnotationType_TargetSpec":
                    affector_id = annotation["affectorId"]
                    affected_ids = annotation["affectedIds"]
                    affector_card = mtga_app.mtga_watch_app.game.find_card_by_iid(
                        affector_id)
                    if not affector_card:
                        # try abilitiy
                        details = annotation["details"]
                        grpid = None
                        for detail in details:
                            if detail["key"] == "grpid":
                                grpid = detail["valueInt32"][0]
                        affector_card = all_mtga_cards.find_one(grpid)
                    targets = []
                    target_texts = []
                    for affected_id in affected_ids:
                        affected_texts = build_event_texts_from_iid_or_grpid(
                            affected_id, mtga_app.mtga_watch_app.game)
                        target_texts.extend(affected_texts)
                        game_obj = mtga_app.mtga_watch_app.game.find_card_by_iid(
                            affected_id)
                        target = game_obj if game_obj else affected_id
                        targets.append(target)
                    if (
                            affector_card, targets
                    ) not in mtga_app.mtga_watch_app.game.recorded_targetspecs:
                        mtga_app.mtga_watch_app.game.recorded_targetspecs.append(
                            (affector_card, targets))
                        affector_texts = build_card_event_texts(
                            affector_card, mtga_app.mtga_watch_app.game)

                        event_texts = [*affector_texts, " targets "]
                        if len(target_texts) > 2:
                            for target in target_texts:
                                event_texts.extend([target, ", "])
                            event_texts.append(target[-2])
                            event_texts.append(", and")
                            event_texts.append(target[-1])
                        elif len(target_texts) > 1:
                            event_texts.extend(
                                [target_texts[-2], " and ", target_texts[-1]])
                        else:
                            event_texts.extend(target_texts)

                        queue_obj = {"game_history_event": event_texts}
                        mtga_app.mtga_watch_app.game.events.append(
                            queue_obj["game_history_event"])
                        general_output_queue.put(queue_obj)
                if annotation_type == "AnnotationType_ResolutionComplete":
                    try:
                        affector_id = annotation["affectorId"]
                        card = mtga_app.mtga_watch_app.game.find_card_by_iid(
                            affector_id)
                        if isinstance(card, Ability):
                            # card resolutions are handled in annotations below
                            __unused_affected_ids = annotation["affectedIds"]
                            grpid = None
                            details = annotation["details"]
                            for detail in details:
                                if detail["key"] == "grpid":
                                    grpid = detail["valueInt32"][0]
                            resolved_texts = build_event_texts_from_iid_or_grpid(
                                affector_id, mtga_app.mtga_watch_app.game,
                                grpid)
                            event_texts = [*resolved_texts, " resolves"]
                            queue_obj = {"game_history_event": event_texts}
                            mtga_app.mtga_watch_app.game.events.append(
                                queue_obj["game_history_event"])
                            general_output_queue.put(queue_obj)
                    except:
                        app.mtga_app.mtga_logger.error(
                            "{}Exception @ count {}".format(
                                util.ld(True),
                                app.mtga_app.mtga_watch_app.error_count))
                        app.mtga_app.mtga_logger.error(
                            "{}parsers:parse_game_state_message - error parsing annotation:"
                            .format(util.ld(True)))
                        app.mtga_app.mtga_logger.error(
                            pprint.pformat(annotation))
                        app.mtga_app.mtga_watch_app.send_error(
                            "Exception during parse AnnotationType_ResolutionComplete. Check log for more details"
                        )

        if 'gameObjects' in message.keys():
            game_objects = message['gameObjects']
            for object in game_objects:
                card_id = object['grpId']
                instance_id = object['instanceId']
                if instance_id in mtga_app.mtga_watch_app.game.ignored_iids:
                    continue
                owner = object['controllerSeatId']
                type = object["type"]
                zone = object['zoneId']
                if type not in [
                        "GameObjectType_Card", "GameObjectType_Ability",
                        "GameObjectType_SplitCard"
                ]:
                    mtga_app.mtga_watch_app.game.ignored_iids.add(instance_id)
                else:
                    player, zone = mtga_app.mtga_watch_app.game.get_owner_zone_tup(
                        zone)
                    if zone:
                        if not player:
                            player = mtga_app.mtga_watch_app.game.hero
                            # if zone is shared, don't care what player we use to put this card into it
                        assert isinstance(player, Player)
                        if type in [
                                "GameObjectType_Card",
                                "GameObjectType_SplitCard"
                        ]:
                            player.put_instance_id_in_zone(
                                instance_id, owner, zone)
                            zone.match_game_id_to_card(instance_id, card_id)
                        elif type == "GameObjectType_Ability":
                            source_instance_id = object['parentId']
                            source_grp_id = object['objectSourceGrpId']
                            ability_name = all_mtga_cards.find_one(card_id)
                            ability = Ability(ability_name, source_grp_id,
                                              source_instance_id, card_id,
                                              owner, instance_id)
                            zone.abilities.append(ability)
                if "attackState" in object and object[
                        "attackState"] == "AttackState_Attacking":
                    card = mtga_app.mtga_watch_app.game.find_card_by_iid(
                        instance_id)
                    limit_tuple = (mtga_app.mtga_watch_app.game.turn_number,
                                   "attacks", card)
                    if limit_tuple not in mtga_app.mtga_watch_app.game.recorded_targetspecs:
                        mtga_app.mtga_watch_app.game.recorded_targetspecs.append(
                            limit_tuple)
                        card_texts = build_event_texts_from_iid_or_grpid(
                            instance_id, mtga_app.mtga_watch_app.game)
                        event_texts = [*card_texts, " attacking"]
                        queue_obj = {"game_history_event": event_texts}
                        mtga_app.mtga_watch_app.game.events.append(
                            queue_obj["game_history_event"])
                        general_output_queue.put(queue_obj)
                if "blockState" in object and object[
                        "blockState"] == "BlockState_Blocking":
                    card = mtga_app.mtga_watch_app.game.find_card_by_iid(
                        instance_id)
                    block_info = object["blockInfo"]
                    attacker_list = block_info["attackerIds"]
                    for attacker in attacker_list:
                        attacker_card = mtga_app.mtga_watch_app.game.find_card_by_iid(
                            attacker)
                        limit_tuple = (
                            mtga_app.mtga_watch_app.game.turn_number, "blocks",
                            card, attacker_card)
                        if limit_tuple not in mtga_app.mtga_watch_app.game.recorded_targetspecs:
                            mtga_app.mtga_watch_app.game.recorded_targetspecs.append(
                                limit_tuple)
                            attacker_texts = build_event_texts_from_iid_or_grpid(
                                attacker, mtga_app.mtga_watch_app.game)
                            blocker_texts = build_event_texts_from_iid_or_grpid(
                                instance_id, mtga_app.mtga_watch_app.game)

                            event_texts = [
                                *blocker_texts, " blocks ", *attacker_texts
                            ]
                            queue_obj = {"game_history_event": event_texts}
                            mtga_app.mtga_watch_app.game.events.append(
                                queue_obj["game_history_event"])
                            general_output_queue.put(queue_obj)
        if 'zones' in message.keys():
            cards_to_remove_from_zones = {}
            for zone in message['zones']:
                try:
                    removable = parse_zone(zone)
                    if removable:
                        cards_to_remove_from_zones[zone["zoneId"]] = removable
                except:
                    app.mtga_app.mtga_logger.error(
                        "{}Exception @ count {}".format(
                            util.ld(True),
                            app.mtga_app.mtga_watch_app.error_count))
                    app.mtga_app.mtga_logger.error(
                        "{}error parsing zone:".format(util.ld(True)))
                    app.mtga_app.mtga_logger.error(pprint.pformat(zone))
                    app.mtga_app.mtga_watch_app.send_error(
                        "Exception during parse zone. Check log for more details"
                    )
                    import traceback
                    exc = traceback.format_exc()
                    app.mtga_app.mtga_logger.error(exc)
            for zone_id in cards_to_remove_from_zones.keys():
                remove_these = cards_to_remove_from_zones[zone_id]
                player, zone = mtga_app.mtga_watch_app.game.get_owner_zone_tup(
                    zone_id)
                for card in remove_these:
                    if card in zone.cards:
                        zone.cards.remove(card)
        if message[
                "type"] == "GameStateType_Diff" and "players" in message.keys(
                ):
            players = message["players"]
            for player in players:
                seat = player["systemSeatNumber"]
                life_total = player["lifeTotal"]
                player_obj = mtga_app.mtga_watch_app.game.get_player_in_seat(
                    seat)
                if player_obj.current_life_total != life_total:
                    player_is_hero = mtga_app.mtga_watch_app.game.hero == player_obj
                    player_life_text_type = "{}".format(
                        "hero" if player_is_hero else "opponent")
                    player_life_text = build_event_text(
                        player_obj.player_name, player_life_text_type)
                    event_texts = [
                        player_life_text, "'s life total changed ",
                        "{} -> {}".format(player_obj.current_life_total,
                                          life_total)
                    ]
                    queue_obj = {"game_history_event": event_texts}
                    mtga_app.mtga_watch_app.game.events.append(
                        queue_obj["game_history_event"])
                    general_output_queue.put(queue_obj)
                    player_obj.current_life_total = life_total
        # AFTER we've processed gameObjects, look for actions that should go in the log
        # If this code is in the block above gameObjects, then we will end up with lots of
        # "unknown" cards for opponent cards and actions
        if 'annotations' in message.keys():
            for annotation in message['annotations']:
                annotation_type = annotation['type'][0]
                if annotation_type == "AnnotationType_ZoneTransfer":
                    if "affectorId" not in annotation.keys():
                        affector_id = 0
                    else:
                        affector_id = annotation["affectorId"]
                    affected_ids = annotation["affectedIds"]
                    details = annotation["details"]
                    zone_src, zone_dest, category = None, None, None
                    for detail in details:
                        if detail["key"] == "zone_src":
                            zone_src = detail["valueInt32"][0]
                        if detail["key"] == "zone_dest":
                            zone_dest = detail["valueInt32"][0]
                        if detail["key"] == "category":
                            category = detail["valueString"][0]

                    card = mtga_app.mtga_watch_app.game.find_card_by_iid(
                        affected_ids[0])
                    if affector_id == 0:
                        affector_id = card.owner_seat_id
                    player_texts = build_event_texts_from_iid_or_grpid(
                        affector_id, mtga_app.mtga_watch_app.game)
                    annotation_texts = build_event_texts_from_iid_or_grpid(
                        affected_ids[0], mtga_app.mtga_watch_app.game)

                    if category == "PlayLand":
                        event_texts = [
                            *player_texts, " plays ", *annotation_texts
                        ]
                        queue_obj = {"game_history_event": event_texts}
                        mtga_app.mtga_watch_app.game.events.append(
                            queue_obj["game_history_event"])
                        general_output_queue.put(queue_obj)
                    elif category == "Draw":
                        if affector_id > 2:
                            owner = card.owner_seat_id
                            player_texts.extend([
                                ": ", *build_event_texts_from_iid_or_grpid(
                                    owner, mtga_app.mtga_watch_app.game)
                            ])
                        if card.pretty_name == "unknown":
                            event_texts = [*player_texts, " draws"]
                        else:
                            event_texts = [
                                *player_texts, " draws ", *annotation_texts
                            ]
                        queue_obj = {"game_history_event": event_texts}
                        mtga_app.mtga_watch_app.game.events.append(
                            queue_obj["game_history_event"])
                        general_output_queue.put(queue_obj)
                        # build draw log event
                    elif category == "CastSpell":
                        event_texts = [
                            *player_texts, " casts ", *annotation_texts
                        ]
                        queue_obj = {"game_history_event": event_texts}
                        mtga_app.mtga_watch_app.game.events.append(
                            queue_obj["game_history_event"])
                        general_output_queue.put(queue_obj)
                        # build draw log event
                        # TODO: see if this is redundant
                    elif category == "Countered":
                        event_texts = [
                            *player_texts, " counters ", *annotation_texts
                        ]
                        queue_obj = {"game_history_event": event_texts}
                        mtga_app.mtga_watch_app.game.events.append(
                            queue_obj["game_history_event"])
                        general_output_queue.put(queue_obj)
                    elif category == "Resolve":
                        event_texts = [*annotation_texts, " resolves"]
                        queue_obj = {"game_history_event": event_texts}
                        mtga_app.mtga_watch_app.game.events.append(
                            queue_obj["game_history_event"])
                        general_output_queue.put(queue_obj)
                    elif category == "Exile":
                        event_texts = [
                            *player_texts, " exiles ", *annotation_texts
                        ]
                        queue_obj = {"game_history_event": event_texts}
                        mtga_app.mtga_watch_app.game.events.append(
                            queue_obj["game_history_event"])
                        general_output_queue.put(queue_obj)
                    # TODO: category == "Put" ?
                    elif zone_dest == 37 or zone_dest == 33:  # TODO: get rid of this hardcoded bs
                        event_texts = [
                            *annotation_texts, " sent to graveyard ",
                            "(" + category + ")"
                        ]
                        queue_obj = {"game_history_event": event_texts}
                        mtga_app.mtga_watch_app.game.events.append(
                            queue_obj["game_history_event"])
                        general_output_queue.put(queue_obj)
                kt = KillableTailer(log_file, queues.all_die_queue)
                kt.seek_end()
                for line in kt.follow(1):
                    if line.strip() == "":
                        if "{" in current_block:  # try to speed up debug runs by freeing up json watcher task
                            # which is likely the slowest
                            queues.block_read_queue.put(current_block)
                        current_block = ""
                    else:
                        current_block += line.strip() + "\n"
                    if not all_die_queue.empty():
                        break
        else:
            general_output_queue.put({
                "error": "NoLogException",
                "msg":
                "No log file present. Please run MTGA at least once before launching MTGA Tracker.",
                "count": 1
            })

    queues.block_read_queue.put(None)

    block_watch_process.join()
    json_watch_process.join()
    websocket_thread.join()
    start_server.ws_server.close()
    if mouse_thread:
        mouse_thread.join()
        print("mouse joined!")
    while queues.json_blob_queue.qsize():
        queues.json_blob_queue.get()
Example #13
0
def parse_rank_updated(blob):
    general_output_queue.put({"rank_change": blob})
Example #14
0
def parse_game_state_message(message, timestamp=None):
    # DOM: ok
    import app.mtga_app as mtga_app
    with mtga_app.mtga_watch_app.game_lock:  # the game state may become inconsistent in between these steps, so lock it
        if "turnInfo" in message.keys():
            if "turnNumber" in message["turnInfo"].keys():
                player = app.mtga_app.mtga_watch_app.game.get_player_in_seat(message["turnInfo"]["activePlayer"])
                if "decisionPlayer" in message["turnInfo"].keys():
                    decisionPlayer = app.mtga_app.mtga_watch_app.game.get_player_in_seat(message["turnInfo"]["decisionPlayer"])
                else:
                    decisionPlayer = app.mtga_app.mtga_watch_app.game.last_decision_player
                if timestamp:
                    now = datetime.datetime.now()
                    if app.mtga_app.mtga_watch_app.game.last_log_timestamp is None:
                        app.mtga_app.mtga_watch_app.game.last_log_timestamp = timestamp
                        app.mtga_app.mtga_watch_app.game.last_measured_timestamp = now
                        app.mtga_app.mtga_watch_app.game.log_start_time = timestamp
                        app.mtga_app.mtga_watch_app.game.last_decision_player = decisionPlayer

                    measured_time_diff = now - app.mtga_app.mtga_watch_app.game.last_measured_timestamp
                    log_time_diff = timestamp - app.mtga_app.mtga_watch_app.game.last_log_timestamp

                    if measured_time_diff > log_time_diff:
                        log_time_diff = measured_time_diff  # some turns are really fast, and the logs see it as 0 seconds. Add what we measured instead,

                    app.mtga_app.mtga_watch_app.game.last_log_timestamp = timestamp
                    app.mtga_app.mtga_watch_app.game.last_measured_timestamp = now
                    ct_obj = {"turnInfo": message["turnInfo"],
                              "diff": log_time_diff,
                              "countsAgainst": app.mtga_app.mtga_watch_app.game.last_decision_player}
                    app.mtga_app.mtga_watch_app.game.chess_timer.append(ct_obj)
                    general_output_queue.put({"decisionPlayerChange": True, "heroIsDeciding": decisionPlayer == app.mtga_app.mtga_watch_app.game.hero})
                    app.mtga_app.mtga_watch_app.game.last_decision_player = decisionPlayer
                app.mtga_app.mtga_watch_app.game.turn_number = message["turnInfo"]["turnNumber"]
                other_player_seat = 2 if message["turnInfo"]["activePlayer"] == 1 else 1
                other_player = app.mtga_app.mtga_watch_app.game.get_player_in_seat(other_player_seat)
                app.mtga_app.mtga_watch_app.game.current_player = player.player_name
                if not app.mtga_app.mtga_watch_app.game.on_the_play:
                    if message["turnInfo"]["turnNumber"] % 2 == 1:
                        app.mtga_app.mtga_watch_app.game.on_the_play = player.player_name
                    else:
                        app.mtga_app.mtga_watch_app.game.on_the_play = other_player.player_name
                app.mtga_app.mtga_watch_app.game.current_phase = message["turnInfo"]["phase"]
                if "step" in message["turnInfo"].keys():
                    app.mtga_app.mtga_watch_app.game.current_phase += "-{}".format(message["turnInfo"]["step"])
            app.mtga_app.mtga_logger.debug(message["turnInfo"])
        if 'gameInfo' in message.keys():
            if 'matchState' in message['gameInfo']:
                game_number = message['gameInfo']['gameNumber']
                game_player_id = "-game{}-{}".format(game_number, mtga_app.mtga_watch_app.game.hero.player_id)
                match_id_raw = message['gameInfo']['matchID']
                match_id = message['gameInfo']['matchID'] + game_player_id

                if 'results' in message['gameInfo']:
                    results = message['gameInfo']['results']
                    parse_game_results(True, match_id, results)
                if message['gameInfo']['matchState'] == "MatchState_GameInProgress" and \
                        game_number > max(len(app.mtga_app.mtga_watch_app.match.game_results), 1):
                    shared_battlefield = Zone("battlefield")
                    shared_exile = Zone("exile")
                    shared_limbo = Zone("limbo")
                    shared_stack = Zone("stack")
                    new_hero = Player(mtga_app.mtga_watch_app.game.hero.player_name,
                                      mtga_app.mtga_watch_app.game.hero.player_id,
                                      mtga_app.mtga_watch_app.game.hero.seat,
                                      shared_battlefield, shared_exile, shared_limbo, shared_stack,
                                      mtga_app.mtga_watch_app.game.hero._deck_cards)

                    new_oppo = Player(mtga_app.mtga_watch_app.game.opponent.player_name,
                                      mtga_app.mtga_watch_app.game.opponent.player_id,
                                      mtga_app.mtga_watch_app.game.opponent.seat,
                                      shared_battlefield, shared_exile, shared_limbo, shared_stack,
                                      mtga_app.mtga_watch_app.game.opponent._deck_cards)
                    new_hero.is_hero = True
                    if mtga_app.mtga_watch_app.intend_to_join_game_with:
                        new_hero.original_deck = mtga_app.mtga_watch_app.intend_to_join_game_with
                        new_match_id = match_id_raw + "-game{}-{}".format(game_number, new_hero.player_id)
                        mtga_app.mtga_watch_app.game = Game(new_match_id, new_hero, new_oppo, shared_battlefield,
                                                            shared_exile, shared_limbo, shared_stack,
                                                            app.mtga_app.mtga_watch_app.match.event_id,
                                                            app.mtga_app.mtga_watch_app.match.opponent_rank)
        if 'annotations' in message.keys():
            for annotation in message['annotations']:
                annotation_type = annotation['type'][0]
                if annotation_type == 'AnnotationType_ObjectIdChanged':
                    try:
                        original_id = None
                        new_id = None
                        details = annotation['details']
                        for detail in details:
                            if detail['key'] == "orig_id":
                                original_id = detail["valueInt32"][0]
                                mtga_app.mtga_watch_app.game.ignored_iids.add(original_id)
                                # NOTE: at one point Spencer thought it might be correct to ignore these AFTER
                                # parsing the whole gameStateMessage, i.e. put these in a list here, and only add them
                                # to ignored_iid's at the end of this function.
                                #
                                # That was incorrect, and led to cards flip-flopping in the UI.
                                # This is correct as is.
                            elif detail['key'] == "new_id":
                                new_id = detail["valueInt32"][0]
                        card_with_iid = mtga_app.mtga_watch_app.game.find_card_by_iid(original_id)
                        if not card_with_iid:  # no one has ref'd yet, we don't care
                            continue
                        new_card_already_exists = mtga_app.mtga_watch_app.game.find_card_by_iid(new_id)
                        if new_card_already_exists:  # just wipe the old card, the new card is already there
                            assert new_card_already_exists.mtga_id == card_with_iid.mtga_id or -1 in [new_card_already_exists.mtga_id, card_with_iid.mtga_id], "{} / {}".format(new_card_already_exists.mtga_id , card_with_iid.mtga_id)
                            card_with_iid.mtga_id = -1
                        else:
                            card_with_iid.previous_iids.append(original_id)
                            card_with_iid.game_id = new_id
                    except:
                        app.mtga_app.mtga_logger.error("{}Exception @ count {}".format(util.ld(True), app.mtga_app.mtga_watch_app.error_count))
                        app.mtga_app.mtga_logger.error("{}parsers:parse_game_state_message - error parsing annotation:".format(util.ld(True)))
                        app.mtga_app.mtga_logger.error(pprint.pformat(annotation))
                        app.mtga_app.mtga_watch_app.send_error("Exception during parse annotation. Check log for more details")
        if 'gameObjects' in message.keys():
            game_objects = message['gameObjects']
            for object in game_objects:
                card_id = object['grpId']
                instance_id = object['instanceId']
                if instance_id in mtga_app.mtga_watch_app.game.ignored_iids:
                    continue
                owner = object['controllerSeatId']
                type = object["type"]
                zone = object['zoneId']
                if type != "GameObjectType_Card":
                    mtga_app.mtga_watch_app.game.ignored_iids.add(instance_id)
                else:
                    player, zone = mtga_app.mtga_watch_app.game.get_owner_zone_tup(zone)
                    if zone:
                        if not player:
                            player = mtga_app.mtga_watch_app.game.hero
                            # if zone is shared, don't care what player we use to put this card into it
                        assert isinstance(player, Player)
                        player.put_instance_id_in_zone(instance_id, owner, zone)
                        zone.match_game_id_to_card(instance_id, card_id)
        if 'zones' in message.keys():
            cards_to_remove_from_zones = {}
            for zone in message['zones']:
                try:
                    removable = parse_zone(zone)
                    if removable:
                        cards_to_remove_from_zones[zone["zoneId"]] = removable
                except:
                    app.mtga_app.mtga_logger.error("{}Exception @ count {}".format(util.ld(True), app.mtga_app.mtga_watch_app.error_count))
                    app.mtga_app.mtga_logger.error("{}error parsing zone:".format(util.ld(True)))
                    app.mtga_app.mtga_logger.error(pprint.pformat(zone))
                    app.mtga_app.mtga_watch_app.send_error("Exception during parse zone. Check log for more details")
                    import traceback
                    exc = traceback.format_exc()
                    app.mtga_app.mtga_logger.error(exc)
            for zone_id in cards_to_remove_from_zones.keys():
                remove_these = cards_to_remove_from_zones[zone_id]
                player, zone = mtga_app.mtga_watch_app.game.get_owner_zone_tup(zone_id)
                for card in remove_these:
                    if card in zone.cards:
                        zone.cards.remove(card)