def parse_match_playing(blob): # MatchGameRoomStateType_Playing import app.mtga_app as mtga_app temp_players = {1: {}, 2: {}} match_id = blob['matchGameRoomStateChangedEvent']['gameRoomInfo'][ 'gameRoomConfig']['matchId'] game_room_info = blob["matchGameRoomStateChangedEvent"]["gameRoomInfo"] 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 mtga_app.mtga_watch_app.game = Game(match_id, hero, opponent, shared_battlefield, shared_exile, shared_limbo, shared_stack)
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)
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)
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 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)