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})
def send_error(self, error): self.error_count += 1 general_output_queue.put({ "error": "MTGAWatchError", "msg": error, "count": self.error_count })
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
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
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"]})
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))
def send_message(self, message): general_output_queue.put({"color": "black", "msg": message})
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"])
def pass_through(title, player_key, blob): general_output_queue.put({title: blob, "player_key": player_key})
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()
def parse_rank_updated(blob): general_output_queue.put({"rank_change": blob})
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)