def __init__(self, os, linux_log=board_state): self.os = os parser = LogParser() if self.os == "Windows": log_dir = r"C:\Program Files (x86)\Hearthstone\Logs\Power.log" with io.open(log_dir, "r", encoding='utf8') as logs: k = 0 lines = logs.readlines() for line in lines: k += 1 if "GameState.DebugPrintPower() - CREATE_GAME" in line: start = k parsed_lines = lines[start-1:] self.logs = ''.join(parsed_lines) parser.read(io.StringIO(self.logs)) elif self.os == "Linux": parser.read(io.StringIO(linux_log)) self.linux_log = linux_log parser.flush() packet_tree = parser.games[-1] while True: try: self.game = EntityTreeExporter(packet_tree).export().game except: print("Trapped") continue else: break self.player_names = ['Strafos', 'strafos'] self.card_data = self.get_card_data() self.friendly_player, self.enemy_player = self.read_players()
def update_state(self, hp=1): parser = LogParser() if self.os == "Windows": log_dir = r"C:\Program Files (x86)\Hearthstone\Logs\Power.log" with io.open(log_dir, "r", encoding='utf8') as logs: lines = logs.readlines() self.logs = ''.join(lines) parser.read(io.StringIO(self.logs)) elif self.os == "Linux": parser.read(io.StringIO(self.linux_log)) parser.flush() packet_tree = parser.games[-1] while True: try: self.game = EntityTreeExporter(packet_tree).export().game except: print("Trapped") continue else: break packet_tree = parser.games[-1] self.friendly_player, self.enemy_player = self.read_players() turn = self.get_current_player().name in self.player_names board = self.get_current_board() hand = self.get_current_hand(hp, board) game_step = self.get_game_step() mana = self.get_current_mana() return hand, turn, board, game_step, mana
def test_shuffle_deck(parser): with open(logfile("54613_shuffle_deck.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) exporter.export() assert True
def test_bad_ids(parser): with open(logfile("chaos/change_entity_null_id.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) with pytest.raises(ExporterError): exporter.export()
def test_game_reset(parser): with open(logfile("toki.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) exporter.export() assert True
def test_cached_tag_for_dormant_change(parser): with open(logfile("49534_cached_tag_for_dormant_change.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) exporter.export() assert True
def test_vo_spell(parser): with open(logfile("49534_vo_spell.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) exporter.export() assert True
def test_inferrable_player(parser): with open(logfile("25770_inferrable_player.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) exporter.export() assert True
def test_full_entity_defined_in_subspell(parser): with open(logfile("34104_subspell.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) exporter.export() assert True
def test_puzzle_lab(parser): with open(logfile("puzzlelab.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) exporter.export() assert True
def test_puzzle_lab_player(parser: LogParser): with open(logfile("puzzle_player.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree, player_manager=parser.player_manager) exporter.export() assert True
def test_mercenaries_solo_pvp(parser: LogParser): with open(logfile("93227_mercenaries_solo_pvp.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree, player_manager=parser.player_manager) exporter.export() assert True
def test_name_aliasing(parser: LogParser): with open(logfile("88998_missing_player_hash.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree, player_manager=parser.player_manager) exporter.export() assert True
def test_async_player_names(parser: LogParser): with open(logfile("88998_async_player_name.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree, player_manager=parser.player_manager) with pytest.raises(MissingPlayerData): exporter.export()
def test_20457(parser): with open(logfile("20457_broken_names.power.log")) as f: parser.read(f) assert len(parser.games) == 1 packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) game = exporter.export() assert game.game.players[0].name == "Necroessenza" assert game.game.players[1].name == "Heigan l'Impuro"
def test_unknown_human_player(parser: LogParser): with open(logfile("25770_unknown_human_player.power.log")) as f: parser.read(f) packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree, player_manager=parser.player_manager) with pytest.raises(MissingPlayerData): exporter.export()
def test_battlegrounds(parser): with open(logfile("36393_battlegrounds.power.log")) as f: parser.read(f) assert len(parser.games) == 1 packet_tree = parser.games[0] exporter = EntityTreeExporter(packet_tree) game = exporter.export() assert game.game.setup_done assert game.game.players[0].name == "BehEh#1355" assert len([ packet for packet in packet_tree if isinstance(packet, packets.Options) ]) == 96
def __init__(self, filepath="Power.log"): self.parser: LogParser = LogParser() with open(filepath) as f: self.parser.read(f) self.packet_tree: PacketTree = self.parser.games[0] self.exporter: EntityTreeExporter = EntityTreeExporter( self.packet_tree) self.export: EntityTreeExporter = self.exporter.export()
def print_board_at_combat(k): pt = copy.copy(packet_tree) pt.packets = [] current_fight = 0 for i, packet in enumerate(packet_tree.packets): if is_starting_block(packet): current_fight += 1 if current_fight == k: n = 10 if i+n < len(packet_tree.packets): pt.packets = packet_tree.packets[:i+n] break exporter = EntityTreeExporter(pt) export = exporter.export() if hasattr(export, "game"): game = export.game breeky = game.players[0] bob = game.players[1] bob.minions = get_current_minions(bob) breeky.minions = get_current_minions(breeky) print("Bob : ", bob.minions) print("Breeky : ", breeky.minions) print(breeky.minions[0].tags[GameTag.HEALTH])
from hslog.export import FriendlyPlayerExporter, EntityTreeExporter from hearthstone.enums import ( CardType, ChoiceType, GameTag, OptionType, BlockType, PlayReq, PlayState, PowerType, State, Step, Zone ) # logging.getLogger().setLevel(logging.CRITICAL) start = time.time() parser = LogParser() with open("Power.log") as f: parser.read(f) packet_tree = parser.games[0] print("Reading packets took ", time.time()-start, "s.") exporter = EntityTreeExporter(packet_tree) export = exporter.export() game = export.game breeky = game.players[0] bob = game.players[1] print("Export took ", time.time()-start, "s.") def get_current_minions(player): minions = [] for e in player.entities: if e.tags[GameTag.CONTROLLER] == player.tags[GameTag.CONTROLLER] and e.zone == Zone.PLAY: if GameTag.CARDTYPE in e.tags.keys() and e.tags[GameTag.CARDTYPE] == CardType.MINION: minions.append(e) return minions
class GameReader: def __init__(self, os, linux_log=board_state): self.os = os parser = LogParser() if self.os == "Windows": log_dir = r"C:\Program Files (x86)\Hearthstone\Logs\Power.log" with io.open(log_dir, "r", encoding='utf8') as logs: k = 0 lines = logs.readlines() for line in lines: k += 1 if "GameState.DebugPrintPower() - CREATE_GAME" in line: start = k parsed_lines = lines[start-1:] self.logs = ''.join(parsed_lines) parser.read(io.StringIO(self.logs)) elif self.os == "Linux": parser.read(io.StringIO(linux_log)) self.linux_log = linux_log parser.flush() packet_tree = parser.games[-1] while True: try: self.game = EntityTreeExporter(packet_tree).export().game except: print("Trapped") continue else: break self.player_names = ['Strafos', 'strafos'] self.card_data = self.get_card_data() self.friendly_player, self.enemy_player = self.read_players() ## Read card.json for Hearthstone card data def get_card_data(self): if self.os == "Windows": json_dir = r"C:\Users\Zaibo\Desktop\playground\sai\plugins\SerpentHearthstoneGameAgentPlugin\files\cards.json" elif self.os == "Linux": json_dir = "/home/zaibo/code/Hearthstone-Quest-Bot/plugins/SerpentHearthstoneGameAgentPlugin/files/cards.json" else: raise Exception("Invalid OS") with io.open(json_dir, 'r', encoding='utf8') as json_file: json_str = json_file.read() return json.loads(json_str) ## Access json for card data given card_id def get_card_info(self, card_id): for card in self.card_data: if card['id'] == card_id: return card def get_current_player(self): return self.game.current_player def get_game(self): return self.game def get_game_step(self): return self.game.tags.get(GameTag.STEP, 0) # Return Hand of BaseCard objects sorted by increasing cost def get_current_hand(self, hp, board): hand = entities.Hand() for card_in_hand in self.game.in_zone(3): id = card_in_hand.card_id if id: card_info = self.get_card_info(id) card_type = card_info['type'] tags = card_in_hand.tags if card_type == "MINION": minion = entities.HandMinion( card_info['name'], id, tags.get(GameTag.COST, 0), tags.get(GameTag.ZONE_POSITION, 0), card_info['attack'], card_info['health'], card_info.get('mechanics', None)) hand.add_card(minion) elif card_type == "SPELL": spell = entities.HandSpell( card_info['name'], id, tags.get(GameTag.COST, 0), tags.get(GameTag.ZONE_POSITION, 0), card_info.get('playRequirements', None)) hand.add_card(spell) elif card_type == "WEAPON": weapon = entities.HandWeapon( card_info['name'], id, tags.get(GameTag.COST, 0), tags.get(GameTag.ZONE_POSITION, 0), card_info['attack'], card_info['durability']) hand.add_card(weapon) if hp and board.enemy: hero_power = entities.HeroPower('Hero Power', 2, -2, 'Hunter', board.enemy.health) hand.add_card(hero_power) hand.sort_by_cost() return hand ## Create board object def get_current_board(self): minions = [] weapons = [] heroes = [] for board_card in self.game.in_zone(1): if type(board_card) == Card: id = board_card.card_id if id: card_info = self.get_card_info(id) card_type = card_info['type'] if card_info and card_type != "HERO_POWER": tags = board_card.tags if card_type == 'WEAPON': weapon = entities.BoardWeapon( card_info['name'], id, tags.get(GameTag.ZONE_POSITION, 0), board_card.controller, tags.get(GameTag.ATK, 0), tags.get(GameTag.DURABILITY, 0)) weapons.append(weapon) elif card_type == 'MINION': exhaust = tags.get(GameTag.EXHAUSTED, not tags.get(GameTag.CHARGE, 0)) if tags.get(GameTag.FROZEN): exhaust = 1 minion = entities.BoardMinion( card_info['name'], id, tags[GameTag.ZONE_POSITION], board_card.controller, tags.get(GameTag.ATK, 0), tags.get(GameTag.HEALTH, 0), tags.get(GameTag.TAUNT, 0), exhaust) minions.append(minion) elif card_type == 'HERO': hero = entities.BoardHero( card_info['name'], id, None, board_card.controller, tags.get(GameTag.HEALTH, 30) - tags.get(GameTag.DAMAGE, 0), tags.get(GameTag.EXHAUSTED, 0) ) heroes.append(hero) return entities.Board(minions, heroes, weapons) ## Update Game object by rereading logs def update_state(self, hp=1): parser = LogParser() if self.os == "Windows": log_dir = r"C:\Program Files (x86)\Hearthstone\Logs\Power.log" with io.open(log_dir, "r", encoding='utf8') as logs: lines = logs.readlines() self.logs = ''.join(lines) parser.read(io.StringIO(self.logs)) elif self.os == "Linux": parser.read(io.StringIO(self.linux_log)) parser.flush() packet_tree = parser.games[-1] while True: try: self.game = EntityTreeExporter(packet_tree).export().game except: print("Trapped") continue else: break packet_tree = parser.games[-1] self.friendly_player, self.enemy_player = self.read_players() turn = self.get_current_player().name in self.player_names board = self.get_current_board() hand = self.get_current_hand(hp, board) game_step = self.get_game_step() mana = self.get_current_mana() return hand, turn, board, game_step, mana def read_players(self): friendly = None enemy = None for player in self.game.players: if player.name in self.player_names: friendly = player else: enemy = player return friendly, enemy def get_current_mana(self): if self.friendly_player: tags = self.friendly_player.tags return tags.get(GameTag.RESOURCES, 0) - tags.get(GameTag.RESOURCES_USED, 0) + tags.get(GameTag.TEMP_RESOURCES, 0) return 0
def test_replays_api(auth_token, client, mocker): mocker.patch("hsreplaynet.api.serializers.replays.classify_deck", return_value=1) upload_event = UploadEvent( id="1", shortid="ccSgiGQaenVzXzwGYbaUTPGrv", token_uuid=auth_token.key, ) path = os.path.join(LOG_DATA_DIR, "hsreplaynet-tests", "replays", "whizbang_friendly.annotated.xml") with open(path, "r") as f: replay = HSReplayDocument.from_xml_file(f) packet_tree = replay.to_packet_tree()[0] meta = { "game_type": enums.BnetGameType.BGT_RANKED_STANDARD, "ladder_season": 42, "format": enums.FormatType.FT_STANDARD, "friendly_player": 1, "reconnecting": False, "scenario_id": 2, "start_time": packet_tree.start_time, "end_time": packet_tree.end_time, "player1": { "rank": 20, "stars": 30, "deck": [ "BOT_447", "BOT_447", "EX1_319", "EX1_319", "LOOT_014", "LOOT_014", "BOT_263", "BOT_263", "BOT_568", "CS2_065", "CS2_065", "EX1_596", "EX1_596", "BOT_443", "BOT_443", "LOOT_013", "LOOT_013", "BOT_224", "BOT_224", "BOT_226", "BOT_226", "ICC_466", "ICC_466", "ICC_075", "ICC_075", "EX1_310", "EX1_310", "BOT_521", "BOT_521", "ICC_831", ], "deck_id": 1337, "cardback": 136, }, "player2": { "rank": 19, "cardback": 138, }, } entity_tree = EntityTreeExporter(packet_tree).export().game replay_xml = "foo.xml" replay = create_dynamodb_game_replay(upload_event, meta, entity_tree, replay_xml) replay.save() user_id = auth_token.user.id response = client.get("/api/v1/replays/?user_id=%d" % (user_id)) assert response.status_code == status.HTTP_200_OK payload = _parse_streaming_json(response.streaming_content)[0] assert payload["user_id"] == user_id assert payload["match_start"] == "2018-08-08T21:20:02.610000Z" assert payload["match_end"] == "2018-08-08T21:31:10.236000Z" assert payload["shortid"] == "ccSgiGQaenVzXzwGYbaUTPGrv" assert payload["game_type"] == enums.BnetGameType.BGT_RANKED_STANDARD assert payload["format_type"] == enums.FormatType.FT_STANDARD assert payload["friendly_player_account_hi"] == "144115193835963207" assert payload["friendly_player_account_lo"] == "127487329" assert payload["friendly_player_battletag"] == "Masture#1176" assert payload["friendly_player_rank"] == 20 assert payload["friendly_player_archetype_id"] == 1 assert payload["opponent_account_hi"] == "144115193835963207" assert payload["opponent_account_lo"] == "50318740" assert payload["opponent_battletag"] == "GinyuGamer#1677" assert payload["opponent_rank"] == 19 assert payload["opponent_archetype_id"] is None assert payload["replay_xml"] == "foo.xml" assert payload["disconnected"] is False assert payload["reconnecting"] is False assert payload["visibility"] == Visibility.Public assert payload["views"] == 0
def scrape_board_state(self, parser: LogParser): packet_tree = parser.games[len(parser.games) - 1] exporter = EntityTreeExporter(packet_tree) export = exporter.export() minions = [] enchantments = [] hero_powers = [] entities = {} # For Debug inspection friendly_player = friendly_hero = None enemy_player = enemy_hero = None for e in export.game.entities: entities[e.id] = e if e.type == CardType.ENCHANTMENT: enchantments.append(e) if e.zone == Zone.PLAY: if e.type == CardType.HERO_POWER: hero_powers.append(e) elif e.type == CardType.MINION: minions.append(e) elif e.type == CardType.PLAYER: if e.is_ai: enemy_player = e else: friendly_player = e elif e.type == CardType.HERO: if e.controller == enemy_player: enemy_hero = e elif e.controller == friendly_player: friendly_hero = e friendlyMinions = [] enemyMinions = [] for minion in minions: # Create list to hold deathrattle entity ids to convert to ghastcoiler ones minion.deathrattle_ids = [] if GameTag.ATK not in minion.tags: # Minions with 0 power dont have an ATK tag. Make sure it exists here minion.tags[GameTag.ATK] = 0 enemyMinions.append( minion) if minion.controller.is_ai else friendlyMinions.append( minion) self.attach_enchantments(enchantments, minions) friendlyMinions.sort( key=lambda minion: minion.tags[GameTag.ZONE_POSITION]) enemyMinions.sort( key=lambda minion: minion.tags[GameTag.ZONE_POSITION]) friendly_health, friendly_tech_level = self.get_hero_tags( friendly_hero.tags) enemy_health, enemy_tech_level = self.get_hero_tags(enemy_hero.tags) state = BoardState(friendlyBoard=friendlyMinions, friendlyPlayerHealth=friendly_health, friendlyTechLevel=friendly_tech_level, friendlyHero=self.get_hero(friendly_hero.card_id), enemyBoard=enemyMinions, enemyPlayerHealth=enemy_health, enemyTechLevel=enemy_tech_level, enemyHero=self.get_hero(enemy_hero.card_id)) self.apply_hero_powers(state, hero_powers) return state
def test_create_dynamodb_game_replay(auth_token): upload_event = UploadEvent( id="1", shortid="ccSgiGQaenVzXzwGYbaUTPGrv", token_uuid=auth_token.key, ) meta = { "game_type": enums.BnetGameType.BGT_RANKED_STANDARD, "ladder_season": 42, "format": enums.FormatType.FT_STANDARD, "friendly_player": 1, "reconnecting": False, "scenario_id": 2, "start_time": datetime(year=2018, month=8, day=8, hour=19, minute=20, second=2, microsecond=606936), "end_time": datetime(year=2018, month=8, day=8, hour=19, minute=31, second=10), "player1": { "rank": 20, "stars": 30, "deck": [ "BOT_447", "BOT_447", "EX1_319", "EX1_319", "LOOT_014", "LOOT_014", "BOT_263", "BOT_263", "BOT_568", "CS2_065", "CS2_065", "EX1_596", "EX1_596", "BOT_443", "BOT_443", "LOOT_013", "LOOT_013", "BOT_224", "BOT_224", "BOT_226", "BOT_226", "ICC_466", "ICC_466", "ICC_075", "ICC_075", "EX1_310", "EX1_310", "BOT_521", "BOT_521", "ICC_831", ], "deck_id": 1337, "cardback": 136, }, "player2": { "rank": 19, "cardback": 138, }, } path = os.path.join(LOG_DATA_DIR, "hsreplaynet-tests", "replays", "whizbang_friendly.annotated.xml") with open(path, "r") as f: replay = HSReplayDocument.from_xml_file(f) packet_tree = replay.to_packet_tree()[0] entity_tree = EntityTreeExporter(packet_tree).export().game replay_xml = "foo.xml" replay = create_dynamodb_game_replay(upload_event, meta, entity_tree, replay_xml) assert replay assert replay.user_id == auth_token.user.id assert replay.match_start == 1533756002606 assert replay.match_end == 1533756670000 assert replay.short_id == upload_event.shortid assert replay.digest is None assert replay.game_type == enums.BnetGameType.BGT_RANKED_STANDARD assert replay.format_type == enums.FormatType.FT_STANDARD assert replay.game_type_match_start == "2:1533756002606" assert replay.ladder_season == 42 assert replay.brawl_season is None assert replay.scenario_id == 2 assert replay.num_turns == 31 assert replay.friendly_player_account_hilo == "144115193835963207_127487329" assert replay.friendly_player_battletag == "Masture#1176" assert replay.friendly_player_is_first assert replay.friendly_player_rank == 20 assert replay.friendly_player_rank_stars == 30 assert replay.friendly_player_legend_rank is None assert replay.friendly_player_wins is None assert replay.friendly_player_losses is None assert replay.friendly_player_class == enums.CardClass.WARLOCK assert replay.friendly_player_deck == \ "AAECAf0GApfTAo+CAw4w9wTCCPYIm8sC980C8dAC8tAC9PcC0/gCqvkCt/0Cw/0C+v4CAA==" assert replay.friendly_player_blizzard_deck_id == 1337 assert replay.friendly_player_cardback_id == 136 assert replay.friendly_player_final_state == enums.PlayState.WON assert replay.opponent_account_hilo == "144115193835963207_50318740" assert replay.opponent_battletag == "GinyuGamer#1677" assert not replay.opponent_is_ai assert replay.opponent_rank == 19 assert replay.opponent_legend_rank is None assert replay.opponent_class == enums.CardClass.PALADIN assert replay.opponent_hero == 671 assert replay.opponent_revealed_deck == \ "AAECAZ8FDYoGlgm5wQLjywKc4gKL5QKb8AKl9QKE/ALW/gKggAPMgQPeggMEiMcC/PwC4f4CkYADAA==" assert replay.opponent_predicted_deck is None assert replay.opponent_final_state == enums.PlayState.LOST assert replay.replay_xml == replay_xml assert not replay.disconnected assert not replay.reconnecting assert replay.hslog_version assert replay.visibility == auth_token.user.default_replay_visibility assert replay.views == 0