예제 #1
0
 def load_settings(self):
     mtga_logger.info("{}loading settings".format(util.ld()))
     os.makedirs(self._settings_path, exist_ok=True)
     if not os.path.exists(self._settings_json_path):
         with open(self._settings_json_path, 'w') as wp:
             json.dump({"player_decks": {}, "player_id": None}, wp)
     try:
         with open(self._settings_json_path) as rp:
             settings = json.load(rp)
     except:
         mtga_logger.error("{}had to move settings.json -> settings.json.bak, trying again...".format(util.ld()))
         # mtga_watch_app.send_error("Error loading settings; had to move settings.json -> settings.json.bak")
         if os.path.exists(self._settings_json_path + ".bak"):
             os.remove(self._settings_json_path + ".bak")
         os.rename(self._settings_json_path, self._settings_json_path + ".bak")
         return self.load_settings()
     if "collection" in settings and settings["collection"]:
         self.collection = settings["collection"]
     if "player_id" in settings and settings["player_id"]:
         self.player_id = settings["player_id"]
     if "player_decks" in settings and settings["player_decks"]:
         for deck_id in settings["player_decks"]:
             self.player_decks[deck_id] = Deck.from_dict(settings['player_decks'][deck_id])
     mtga_logger.debug("{}queue put from settings {}".format(util.ld(), id(decklist_change_queue)))
     new_dl = {k: v.to_serializable(transform_to_counted=True) for k, v in self.player_decks.items()}
     if not new_dl:
         new_dl = {"no_decks_defined": True}
     decklist_change_queue.put(new_dl)
예제 #2
0
def dispatch_gre_to_client(blob):
    client_messages = blob["greToClientEvent"]['greToClientMessages']
    dont_care_types = ["GREMessageType_UIMessage"]
    for message in client_messages:
        message_type = message["type"]
        if message_type in dont_care_types:
            pass
        elif message_type in ["GREMessageType_GameStateMessage", "GREMessageType_QueuedGameStateMessage"]:
            game_state_message = message['gameStateMessage']
            try:
                parsers.parse_game_state_message(game_state_message, blob["timestamp"] if "timestamp" in blob.keys() else None)
            except:
                import traceback
                exc = traceback.format_exc()
                stack = traceback.format_stack()
                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(exc)
                app.mtga_app.mtga_logger.error(stack)
                app.mtga_app.mtga_watch_app.send_error("Exception during parse game state. Check log for more details")
        elif message_type == "GREMessageType_MulliganReq":
            try:
                parsers.parse_mulligan_req_message(message, blob["timestamp"] if "timestamp" in blob.keys() else None)
            except:
                import traceback
                exc = traceback.format_exc()
                stack = traceback.format_stack()
                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(exc)
                app.mtga_app.mtga_logger.error(stack)
                app.mtga_app.mtga_watch_app.send_error("Exception during parse game state. Check log for more details")
예제 #3
0
def dispatch_gre_to_client(blob):
    client_messages = blob["greToClientEvent"]['greToClientMessages']
    dont_care_types = ["GREMessageType_UIMessage"]

    action_required_types = ["GREMessageType_PayCostsReq",
                             "GREMessageType_ActionsAvailableReq",
                             "GREMessageType_DeclareAttackersReq",
                             "GREMessageType_DeclareBlockersReq",
                             "GREMessageType_SelectNReq"]

    prompt_action_required_types = ["GREMessageType_PromptReq"]

    doing_action = False

    for message in client_messages:
        message_type = message["type"]
        if message_type in dont_care_types:
            pass
        elif message_type in action_required_types:
            doing_action = True
            parsers.parse_action_required_message(message)
        elif message_type in prompt_action_required_types:
            doing_action = True
            parsers.parse_prompt_action_required(message)
        elif message_type in ["GREMessageType_GameStateMessage", "GREMessageType_QueuedGameStateMessage"]:
            game_state_message = message['gameStateMessage']
            try:
                parsers.parse_game_state_message(game_state_message,
                                                 blob["timestamp"] if "timestamp" in blob.keys() else None)
            except:
                import traceback
                exc = traceback.format_exc()
                stack = traceback.format_stack()
                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(exc)
                app.mtga_app.mtga_logger.error(stack)
                app.mtga_app.mtga_watch_app.send_error("Exception during parse game state. Check log for more details")
        elif message_type == "GREMessageType_MulliganReq":
            try:
                parsers.parse_mulligan_req_message(message, blob["timestamp"] if "timestamp" in blob.keys() else None)
            except:
                import traceback
                exc = traceback.format_exc()
                stack = traceback.format_stack()
                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(exc)
                app.mtga_app.mtga_logger.error(stack)
                app.mtga_app.mtga_watch_app.send_error("Exception during parse game state. Check log for more details")
예제 #4
0
 def save_settings(self):
     mtga_logger.info("{}saving settings".format(util.ld()))
     with open(self._settings_json_path, 'w') as wp:
         write_obj = {
             "player_decks": {d.deck_id: d.to_serializable() for d in self.player_decks.values()},
             "player_id": self.player_id,
             "collection": self.collection,
         }
         json.dump(write_obj, wp)
     mtga_logger.debug("{}queue put from settings {}".format(util.ld(), id(decklist_change_queue)))
     new_dl = {k: v.to_serializable(transform_to_counted=True) for k, v in self.player_decks.items()}
     if not new_dl:
         new_dl = {"no_decks_defined": True}
     decklist_change_queue.put(new_dl)
예제 #5
0
def dispatch_blob(blob):
    seq = blob.get("block_title_sequence", -1)
    log_line = blob.get("log_line", -1)
    if seq:
        app.mtga_app.mtga_logger.debug("{}dispatching seq ({}) / log_line {}".format(util.ld(), seq, log_line))
    if "method" in blob and "jsonrpc" in blob:
        dispatch_jsonrpc_method(blob)
    elif "greToClientEvent" in blob:
        dispatch_gre_to_client(blob)
    elif "clientToGreMessage" in blob:
        dispatch_client_to_gre(blob)
    elif "Deck.GetDeckLists" in blob:  # this looks like it's a response to a jsonrpc method
        parsers.parse_get_decklists(blob)
    elif "block_title" in blob and (blob["block_title"] == "Event.DeckSubmit" or
                                    blob["block_title"] == "Event.GetPlayerCourse"):
        parsers.parse_event_decksubmit(blob)
    elif "block_title" in blob and blob["block_title"] == "PlayerInventory.GetPlayerCardsV3":
        parsers.parse_get_player_cards_v3(blob)
    elif "block_title" in blob and (blob["block_title"] == "Draft.DraftStatus" or
                                    blob["block_title"] == "Draft.MakePick"):
        parsers.parse_draft_status(blob)
    # PlayerInventory.GetPlayerInventory
    elif "block_title" in blob and blob["block_title"] == "PlayerInventory.GetPlayerInventory":
        parsers.pass_through("inventory", blob["playerId"], blob)
    elif "block_title" in blob and blob["block_title"] == "Rank.Updated":
        parsers.pass_through("rank_change", blob["playerId"], blob)
    elif "block_title" in blob and blob["block_title"] == "Inventory.Updated":
        parsers.pass_through("inventory_update", None, blob)
    elif ("block_title" in blob and blob["block_title"] == "ClientToMatchServiceMessageType_ClientToGREMessage" and
          "Payload" in blob and "SubmitDeckResp" in blob['Payload']):
        parsers.parse_sideboard_submit(blob)
    elif "matchGameRoomStateChangedEvent" in blob:
        dispatch_match_gametoom_state_change(blob)
    elif "block_title" in blob and blob["block_title"] == "Event.MatchCreated":
        parsers.parse_match_created(blob)
예제 #6
0
def dispatch_client_to_gre(blob):
    # TODO: seems this is dead code (9/10/18) :(
    client_message = blob['clientToGreMessage']
    message_type = client_message['type']
    dont_care_types = ["ClientMessageType_UIMessage"]
    unknown_types = [
        "ClientMessageType_PerformActionResp",
        "ClientMessageType_DeclareAttackersResp"
        "ClientMessageType_DeclareBlockersResp",
        "ClientMessageType_SetSettingsReq", "ClientMessageType_SelectNResp",
        "ClientMessageType_SelectTargetsResp",
        "ClientMessageType_SubmitTargetsReq",
        "ClientMessageType_SubmitAttackersReq", "ClientMessageType_ConnectReq"
    ]
    if message_type in dont_care_types:
        pass
    elif message_type == "ClientMessageType_MulliganResp":
        parsers.parse_mulligan_response(client_message)
    elif message_type in unknown_types:
        # TODO: log ?
        pass
    else:
        app.mtga_app.mtga_logger.warning(
            "{}WARNING: unknown clientToGreMessage type: {}".format(
                util.ld(), message_type))
예제 #7
0
def dispatch_blob(blob):
    seq = blob.get("block_title_sequence", -1)
    log_line = blob.get("log_line", -1)
    if seq:
        app.mtga_app.mtga_logger.debug(
            "{}dispatching seq ({}) / log_line {}".format(
                util.ld(), seq, log_line))
    if "method" in blob and "jsonrpc" in blob:
        dispatch_jsonrpc_method(blob)
    elif "greToClientEvent" in blob:
        dispatch_gre_to_client(blob)
    elif "clientToGreMessage" in blob:
        dispatch_client_to_gre(blob)
    elif "Deck.GetDeckLists" in blob:  # this looks like it's a response to a jsonrpc method
        parsers.parse_get_decklists(blob)
    elif "block_title" in blob and (blob["block_title"] == "Event.DeckSubmit"
                                    or blob["block_title"]
                                    == "Event.GetPlayerCourse"):
        parsers.parse_event_decksubmit(blob)
    elif "block_title" in blob and blob[
            "block_title"] == "PlayerInventory.GetPlayerCardsV3":
        parsers.parse_get_player_cards_v3(blob)
    elif "block_title" in blob and (blob["block_title"] == "Draft.DraftStatus"
                                    or blob["block_title"]
                                    == "Draft.MakePick"):
        parsers.parse_draft_status(blob)
    elif "matchGameRoomStateChangedEvent" in blob:
        dispatch_match_gametoom_state_change(blob)
예제 #8
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
예제 #9
0
 def check_for_client_id(blob):
     if "authenticateResponse" in blob:
         if "clientId" in blob["authenticateResponse"]:
             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()
예제 #10
0
def dispatch_jsonrpc_method(blob):
    """ route what parser to run on this jsonrpc methoc blob

    :param blob: dict, must contain "method" as top level key
    """
    from app.mtga_app import mtga_watch_app
    dont_care_rpc_methods = [
        'Event.DeckSelect', "Log.Info", "Deck.GetDeckLists",
        "Quest.CompletePlayerQuest"
    ]
    current_method = blob['method']
    request_or_response = blob['request_or_response']
    if current_method in dont_care_rpc_methods:
        pass
    elif current_method == "PlayerInventory.GetPlayerInventory":
        # TODO: keep an eye on this one. currently empty, but maybe it will show up sometime
        app.mtga_app.mtga_logger.info(
            "{}PlayerInventory.GetPlayerInventory found".format(util.ld()))
    else:
        app.mtga_app.mtga_logger.debug(
            "{}not sure what to do with jsonrpc method {}".format(
                util.ld(), current_method))
예제 #11
0
파일: tasks.py 프로젝트: riQQ/mtgatracker
 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"]})
예제 #12
0
def dispatch_blob(blob):
    seq = blob.get("block_title_sequence", -1)
    log_line = blob.get("log_line", -1)
    if seq:
        app.mtga_app.mtga_logger.debug("{}dispatching seq ({}) / log_line {}".format(util.ld(), seq, log_line))
    if "method" in blob and "jsonrpc" in blob:
        dispatch_jsonrpc_method(blob)
    elif "greToClientEvent" in blob:
        dispatch_gre_to_client(blob)
    elif "clientToGreMessage" in blob:
        dispatch_client_to_gre(blob)
    elif "block_title" in blob and blob["block_title"] == "Deck.GetDeckListsV3":
        parsers.parse_get_decklists(blob, version=3)
    elif "block_title" in blob and blob["block_title"] == "Deck.UpdateDeckV3":
        parsers.parse_update_deck_v3(blob)
    elif "block_title" in blob and (blob["block_title"] == "Event.DeckSubmit" or
                                    blob["block_title"] == "Event.GetPlayerCourse"):
        parsers.parse_event_decksubmit(blob)
    elif "block_title" in blob and blob["block_title"] == "Event.DeckSubmitV3":
        parsers.parse_event_decksubmit(blob, version=3)
    elif "block_title" in blob and blob["block_title"] == "Event.GetPlayerCourseV2":
        parsers.parse_event_decksubmit(blob, version=3)
    # TODO: is GetPlayerCoursesV2 useful?
    # elif "block_title" in blob and blob["block_title"] == "Event.GetPlayerCoursesV2":
    #     parsers.parse_player_courses_v2(blob)
    elif "block_title" in blob and blob["block_title"] == "PlayerInventory.GetPlayerCardsV3":
        parsers.parse_get_player_cards_v3(blob["payload"])
    elif "block_title" in blob and (blob["block_title"] == "Draft.DraftStatus" or
                                    blob["block_title"] == "Draft.MakePick"):
        parsers.parse_draft_status(blob)
    elif "block_title" in blob and blob["block_title"] == "PlayerInventory.GetPlayerInventory":
        parsers.pass_through("inventory", blob["payload"]["playerId"], blob["payload"])
    elif "block_title" in blob and blob["block_title"] == "Rank.Updated":
        parsers.pass_through("rank_change", blob["playerId"], blob)
    elif "block_title" in blob and blob["block_title"] == "Inventory.Updated":
        parsers.pass_through("inventory_update", None, blob)
    elif ("block_title" in blob and blob["block_title"] == "ClientToMatchServiceMessageType_ClientToGREMessage" and
          "Payload" in blob and "SubmitDeckResp" in blob['Payload']):
        parsers.parse_sideboard_submit(blob)
    elif "matchGameRoomStateChangedEvent" in blob:
        dispatch_match_gameroom_state_change(blob)
    elif "block_title" in blob and blob["block_title"] == "Event.MatchCreated":
        parsers.parse_match_created(blob)

    # parse on hover event to check what we are looking at, at this moment
    elif "block_title" in blob and blob["block_title"] == "ClientToMatchServiceMessageType_ClientToGREUIMessage":
        parsers.parse_hover(blob)
예제 #13
0
def dispatch_blob(blob):
    seq = blob.get("block_title_sequence", -1)
    if seq:
        app.mtga_app.mtga_logger.debug("{}dispatching seq ({})".format(
            util.ld(), seq))
    if "method" in blob and "jsonrpc" in blob:
        dispatch_jsonrpc_method(blob)
    elif "greToClientEvent" in blob:
        dispatch_gre_to_client(blob)
    elif "clientToGreMessage" in blob:
        dispatch_client_to_gre(blob)
    elif "Deck.GetDeckLists" in blob:  # this looks like it's a response to a jsonrpc method
        parsers.parse_get_decklists(blob)
    elif "block_title" in blob and (blob["block_title"] == "Event.DeckSubmit" or \
                                    blob["block_title"] == "Event.GetPlayerCourse"):
        parsers.parse_event_decksubmit(blob)
    elif "matchGameRoomStateChangedEvent" in blob:
        dispatch_match_gametoom_state_change(blob)
예제 #14
0
파일: tasks.py 프로젝트: riQQ/mtgatracker
def json_blob_reader_task(in_queue, out_queue):
    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"]})

    last_blob = None
    last_decklist = None
    error_count = 0
    while all_die_queue.empty():
        json_recieved = in_queue.get()

        if json_recieved is None:
            out_queue.put(None)
            break

        if last_blob == json_recieved:
            continue  # don't double fire

        # check for decklist changes
        if mtga_watch_app.player_decks != last_decklist:
            last_decklist = mtga_watch_app.player_decks
            decklist_change_queue.put({
                k: v.to_serializable(transform_to_counted=True)
                for k, v in last_decklist.items()
            })

        # check for gamestate changes
        try:
            hero_library_hash = -1
            opponent_hand_hash = -1
            if mtga_watch_app.game:
                hero_library_hash = hash(mtga_watch_app.game.hero.library)
                opponent_hand_hash = hash(mtga_watch_app.game.opponent.hand)

            check_for_client_id(json_recieved)
            dispatchers.dispatch_blob(json_recieved)
            mtga_watch_app.last_blob = json_recieved
            error_count = 0

            hero_library_hash_post = -1
            opponent_hand_hash_post = -1
            if mtga_watch_app.game:
                hero_library_hash_post = hash(mtga_watch_app.game.hero.library)
                opponent_hand_hash_post = hash(
                    mtga_watch_app.game.opponent.hand)
                if hero_library_hash != hero_library_hash_post or opponent_hand_hash != opponent_hand_hash_post:
                    game_state_change_queue.put(mtga_watch_app.game.game_state(
                    ))  # TODO: BREAKPOINT HERE
                if mtga_watch_app.game.final:
                    game_state_change_queue.put({
                        "match_complete":
                        True,
                        "gameID":
                        mtga_watch_app.game.match_id
                    })
        except:
            import traceback
            exc = traceback.format_exc()
            stack = traceback.format_stack()
            mtga_logger.error("{}Exception @ count {}".format(
                util.ld(True), mtga_watch_app.error_count))
            mtga_logger.error(exc)
            mtga_logger.error(stack)
            mtga_watch_app.send_error(
                "Exception during check game state. Check log for more details"
            )
            if error_count > 5:
                mtga_logger.error("{}error count too high; exiting".format(
                    util.ld()))
                return

        last_blob = json_recieved
예제 #15
0
def parse_game_state_message(message):
    # 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 'gameInfo' in message.keys():
            if 'matchState' in message['gameInfo']:
                match_id = message['gameInfo']['matchID']
                game_number = message['gameInfo']['gameNumber']
                if message['gameInfo'][
                        'matchState'] == "MatchState_GameInProgress":
                    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 + "-game{}".format(game_number)
                        mtga_app.mtga_watch_app.game = Game(
                            new_match_id, new_hero, new_oppo,
                            shared_battlefield, shared_exile, shared_limbo,
                            shared_stack)
                if message['gameInfo'][
                        'matchState'] == "MatchState_GameComplete":
                    results = message['gameInfo']['results']
                    parse_game_results(
                        True, match_id + "-game{}".format(game_number),
                        results)
        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)
                            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(),
                                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()))
                        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(),
                            app.mtga_app.mtga_watch_app.error_count))
                    app.mtga_app.mtga_logger.error(
                        "{}error parsing zone:".format(util.ld()))
                    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"
                    )
                    raise
            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)
예제 #16
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)
예제 #17
0
def block_watch_task(in_queue, out_queue):
    while all_die_queue.empty():
        block_recieved = in_queue.get()
        if block_recieved is None:
            out_queue.put(None)
            break
        if "[" not in block_recieved and "{" not in block_recieved:
            continue
        block_lines = block_recieved.split("\n")
        if len(block_lines) < 2:
            continue

        request_or_response = None
        json_str = ""  # hit the ex

        if block_lines[1] and block_lines[1].startswith(
                "==>") or block_lines[1].startswith("<=="):
            """
            these logs looks like:
            
            [UnityCrossThreadLogger]6/7/2018 7:21:03 PM
            ==> Log.Info(530):
            {
                "json": "stuff"
            }
            """
            title_line = block_lines[1]
            block_title = " ".join(title_line.split(" ")[1:]).split("(")[0]
            block_title_seq = None

            if "(" in title_line and ")" in title_line:
                block_title_seq = title_line.split("(")[1].split(")")[
                    0]  # wtf is this thing?
            request_or_response = "response"
            if title_line.startswith("==>"):
                request_or_response = "request"

            json_str = "\n".join(block_lines[2:]).strip()
            if json_str.startswith("["):
                # this is not valid json, we need to surround it with a header such that it's an object instead of a list
                json_str = '{{"{}": {}}}'.format(block_title, json_str)
        elif block_lines[1].strip() == "{":
            """
            these logs look like:
            
            [UnityCrossThreadLogger]6/7/2018 7:21:03 PM: Match to 26848417E29213FE: GreToClientEvent
            {
              "json": "stuff"
            }
            """
            block_title = block_lines[0].split(" ")[-1]
            json_str = "\n".join(block_lines[1:])

        if json_str:
            try:
                blob = json.loads(json_str)
                mtga_logger.info("{}success parsing blob: {}({})".format(
                    util.ld(), block_title, block_title_seq))
                if request_or_response:
                    blob["request_or_response"] = request_or_response
                if block_title:
                    blob["block_title"] = block_title.strip()
                if block_title_seq:
                    blob["block_title_sequence"] = block_title_seq
                out_queue.put(blob)
            except:
                mtga_logger.error("{}Could not parse json_blob `{}`".format(
                    util.ld(), json_str))
                mtga_watch_app.send_error(
                    "Could not parse json_blob {}".format(json_str))
예제 #18
0
def block_watch_task(in_queue, out_queue):
    BLOCK_SEQ = 0
    while all_die_queue.empty():
        block_recieved = in_queue.get()
        if block_recieved is None:
            out_queue.put(None)
            break
        log_line = None
        if isinstance(block_recieved, tuple):
            log_line, block_recieved = block_recieved

        if "[" not in block_recieved and "{" not in block_recieved:
            continue
        block_lines = block_recieved.split("\n")

        request_or_response = None
        json_str = ""  # hit the ex
        timestamp = None
        block_title_seq = None
        if block_lines[0] and block_lines[0].startswith(
                "[UnityCrossThreadLogger]"):
            line = block_lines[0].split("[UnityCrossThreadLogger]")[1]
            if line.startswith("==>") or line.startswith("<=="):
                request_or_response = "response"
                if line.startswith("==>"):
                    request_or_response = "request"
                line = line[4:]
                block_title = line.split(" ")[0]
                indexes = []
                if "{" in line:
                    indexes.append(line.index("{"))
                if "[" in line:
                    indexes.append(line.index("["))
                first_open_bracket = min(indexes)
                json_str = line[first_open_bracket:]
            elif len(block_lines) > 1:
                try:
                    timestamp = dateutil.parser.parse(
                        block_lines[0].split("]")[1].split(": ")[0])
                except:
                    pass
                block_title = block_lines[0].split(" ")[-1]
                json_str = "\n".join(block_lines[1:])
        # I think everything below this is deprecated...
        elif block_lines[1] and block_lines[1].startswith(
                "==>") or block_lines[1].startswith("<=="):
            """
            these logs looks like:
            
            [UnityCrossThreadLogger]6/7/2018 7:21:03 PM
            ==> Log.Info(530):
            {
                "json": "stuff"
            }
            """
            title_line = block_lines[1]
            block_title = " ".join(title_line.split(" ")[1:]).split("(")[0]
            block_title_seq = None

            if "(" in title_line and ")" in title_line:
                block_title_seq = title_line.split("(")[1].split(")")[
                    0]  # wtf is this thing?
            request_or_response = "response"
            if title_line.startswith("==>"):
                request_or_response = "request"

            json_str = "\n".join(block_lines[2:]).strip()
            if json_str.startswith("["):
                # this is not valid json, we need to surround it with a header such that it's an object instead of a list
                json_str = '{{"{}": {}}}'.format(block_title, json_str)
        elif block_lines[1].strip() == "{":
            """ DEPRECATED
            these logs look like:
            
            [UnityCrossThreadLogger]6/7/2018 7:21:03 PM: Match to 26848417E29213FE: GreToClientEvent
            {
              "json": "stuff"
            }
            """
            try:
                timestamp = dateutil.parser.parse(
                    block_lines[0].split("]")[1].split(": ")[0])
            except:
                pass
            block_title = block_lines[0].split(" ")[-1]
            json_str = "\n".join(block_lines[1:])
        elif block_lines[1].strip().endswith("{"):
            """
            these blocks looks like: 
            
            [UnityCrossThreadLogger]7/2/2018 10:27:59 PM
            (-1) Incoming Rank.Updated {
              "json": "stuff
            }
            """
            block_title = block_lines[1].strip().split(" ")[
                -2]  # skip trailing {
            json_str = "{" + "\n".join(
                block_lines[2:]
            )  # cut the first two lines and manually add { back in
        if json_str:
            try:
                blob = json.loads(json_str)
                BLOCK_SEQ += 1
                # useful: next time you're trying to figure out why a blob isn't getting through the queue:
                # if "DirectGame" in json_str and "method" in blob:
                #     import pprint
                #     pprint.pprint(blob)
                if log_line:
                    blob["log_line"] = log_line
                if timestamp:
                    blob["timestamp"] = timestamp
                mtga_logger.info(
                    "{}success parsing blob: {}({}) / log_line {}".format(
                        util.ld(), block_title, block_title_seq, log_line))
                if request_or_response:
                    blob["request_or_response"] = request_or_response
                if block_title:
                    blob["block_title"] = block_title.strip()
                blob["block_title_sequence"] = BLOCK_SEQ
                out_queue.put(blob)
            except Exception as e:
                mtga_logger.error("{}Could not parse json_blob `{}`".format(
                    util.ld(), json_str))
                mtga_watch_app.send_error(
                    "Could not parse json_blob {}".format(json_str))
예제 #19
0
파일: tasks.py 프로젝트: riQQ/mtgatracker
def block_watch_task(in_queue, out_queue):
    while all_die_queue.empty():
        block_recieved = in_queue.get()
        if block_recieved is None:
            out_queue.put(None)
            break

        log_line = None
        if isinstance(block_recieved, tuple):
            log_line, block_recieved = block_recieved

        if "[" not in block_recieved and "{" not in block_recieved:
            continue
        block_lines = block_recieved.split("\n")
        if len(block_lines) < 2:
            continue

        request_or_response = None
        json_str = ""  # hit the ex
        timestamp = None
        block_title_seq = None

        if block_lines[1] and block_lines[1].startswith(
                "==>") or block_lines[1].startswith("<=="):
            """
            these logs looks like:
            
            [UnityCrossThreadLogger]6/7/2018 7:21:03 PM
            ==> Log.Info(530):
            {
                "json": "stuff"
            }
            """
            title_line = block_lines[1]
            block_title = " ".join(title_line.split(" ")[1:]).split("(")[0]
            block_title_seq = None

            if "(" in title_line and ")" in title_line:
                block_title_seq = title_line.split("(")[1].split(")")[
                    0]  # wtf is this thing?
            request_or_response = "response"
            if title_line.startswith("==>"):
                request_or_response = "request"

            json_str = "\n".join(block_lines[2:]).strip()
            if json_str.startswith("["):
                # this is not valid json, we need to surround it with a header such that it's an object instead of a list
                json_str = '{{"{}": {}}}'.format(block_title, json_str)
        elif block_lines[1].strip() == "{":
            """
            these logs look like:
            
            [UnityCrossThreadLogger]6/7/2018 7:21:03 PM: Match to 26848417E29213FE: GreToClientEvent
            {
              "json": "stuff"
            }
            """
            try:
                timestamp = dateutil.parser.parse(
                    block_lines[0].split("]")[1].split(": ")[0])
            except:
                pass
            block_title = block_lines[0].split(" ")[-1]
            json_str = "\n".join(block_lines[1:])
        elif block_lines[0].strip().endswith("{"):
            """
            these blocks looks like: 
            
            [UnityCrossThreadLogger]7/2/2018 10:27:59 PM (-1) Incoming Rank.Updated {
              "json": "stuff
            }
            """
            block_title = block_lines[0].strip().split(" ")[
                -2]  # skip trailing {
            json_str = "{" + "\n".join(
                block_lines[1:]
            )  # just cut the first line and manually add { back in
        if json_str:
            try:
                blob = json.loads(json_str)
                if log_line:
                    blob["log_line"] = log_line
                if timestamp:
                    blob["timestamp"] = timestamp
                mtga_logger.info(
                    "{}success parsing blob: {}({}) / log_line {}".format(
                        util.ld(), block_title, block_title_seq, log_line))
                if request_or_response:
                    blob["request_or_response"] = request_or_response
                if block_title:
                    blob["block_title"] = block_title.strip()
                if block_title_seq:
                    blob["block_title_sequence"] = block_title_seq
                out_queue.put(blob)
            except Exception as e:
                mtga_logger.error("{}Could not parse json_blob `{}`".format(
                    util.ld(), json_str))
                mtga_watch_app.send_error(
                    "Could not parse json_blob {}".format(json_str))
예제 #20
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)