def create_game(self, ts): self.current_game = Game(0) packet_tree = packets.PacketTree(ts) packet_tree.spectator_mode = self.spectator_mode packet_tree.game = self.current_game self.games.append(packet_tree) self.current_block = packet_tree self._entity_packet = packets.CreateGame(ts, self.current_game) self._game_packet = self._entity_packet self.current_block.packets.append(self._entity_packet)
def test_eligible_for_unification(): player1 = Player(1, 1, 144115193835963207, 37760170) player2 = Player(2, 2, 144115193835963207, 153376707) innkeeper = Player(3, 1, 0, 0) ladder_game = Game(1) ladder_game.players = [player1, player2] ai_game = Game(2) ai_game.players = [innkeeper, player1] assert eligible_for_unification(ladder_game, {}) assert not eligible_for_unification(ladder_game, {"reconnecting": True}) assert not eligible_for_unification(ai_game, {}) assert not eligible_for_unification(ai_game, {"reconnecting": True})
def test_entities(): game = Game(1) game.register_entity(game) assert game.find_entity_by_id(1) is game assert game.find_entity_by_id(2) is None
def test_player(): game = Game(1) player = Player(2, 1, 0, 0, "Test Player") player.game = game assert player.starting_hero is None
def game(self): game = Game(1) game.register_entity(game) return game
class PowerHandler(object): def __init__(self): super(PowerHandler, self).__init__() self.current_block = None self._entity_node = None self._metadata_node = None def _check_for_mulligan_hack(self, ts, entity, tag, value): # Old game logs didn't handle asynchronous mulligans properly. # If we're missing an ACTION_END packet after the mulligan SendChoices, # we just close it out manually. if tag == enums.GameTag.MULLIGAN_STATE and value == enums.Mulligan.DEALING: assert self.current_block if isinstance(self.current_block, packets.Block): logging.warning( "WARNING: Broken mulligan nesting. Working around...") self.block_end(ts) def find_callback(self, method): if method == self.parse_method("DebugPrintPower"): return self.handle_data def parse_initial_tag(self, data): sre = TAG_VALUE_RE.match(data) tag, value = sre.groups() return parse_tag(tag, value) def handle_data(self, ts, data): opcode = data.split()[0] if opcode in PowerType.__members__: return self.handle_power(ts, opcode, data) if opcode == "GameEntity": self.flush() sre = GAME_ENTITY_RE.match(data) self.register_game(ts, *sre.groups()) elif opcode == "Player": self.flush() sre = PLAYER_ENTITY_RE.match(data) self.register_player(ts, *sre.groups()) elif opcode.startswith("tag="): tag, value = self.parse_initial_tag(data) self._entity_packet.tags.append((tag, value)) elif opcode.startswith("Info["): if not self._metadata_node: logging.warning("Metadata Info outside of META_DATA: %r", data) return sre = METADATA_INFO_RE.match(data) idx, entity = sre.groups() entity = self.parse_entity(entity) self._metadata_node.info.append(entity) else: raise NotImplementedError(data) def flush(self): if self._entity_node: for k, v in self._entity_packet.tags: self._entity_node.tags[k] = v self._entity_node = None if self._metadata_node: self._metadata_node = None def handle_power(self, ts, opcode, data): self.flush() if opcode == "CREATE_GAME": regex, callback = CREATE_GAME_RE, self.create_game elif opcode in ("ACTION_START", "BLOCK_START"): sre = BLOCK_START_RE.match(data) if sre is None: sre = ACTION_START_OLD_RE.match(data) entity, type, index, target = sre.groups() effectid, effectindex = None, None else: type, entity, effectid, effectindex, target = sre.groups() index = None self.block_start(ts, entity, type, index, effectid, effectindex, target) return elif opcode in ("ACTION_END", "BLOCK_END"): regex, callback = BLOCK_END_RE, self.block_end elif opcode == "FULL_ENTITY": if data.startswith("FULL_ENTITY - Updating"): regex, callback = FULL_ENTITY_UPDATE_RE, self.full_entity_update else: regex, callback = FULL_ENTITY_CREATE_RE, self.full_entity elif opcode == "SHOW_ENTITY": regex, callback = SHOW_ENTITY_RE, self.show_entity elif opcode == "HIDE_ENTITY": regex, callback = HIDE_ENTITY_RE, self.hide_entity elif opcode == "CHANGE_ENTITY": regex, callback = CHANGE_ENTITY_RE, self.change_entity elif opcode == "TAG_CHANGE": regex, callback = TAG_CHANGE_RE, self.tag_change elif opcode == "META_DATA": regex, callback = META_DATA_RE, self.meta_data else: raise NotImplementedError(data) sre = regex.match(data) if not sre: logging.warning("Could not correctly parse %r", data) return callback(ts, *sre.groups()) # Messages def create_game(self, ts): self.current_game = Game(0) packet_tree = packets.PacketTree(ts) packet_tree.spectator_mode = self.spectator_mode packet_tree.game = self.current_game self.games.append(packet_tree) self.current_block = packet_tree self._entity_packet = packets.CreateGame(ts, self.current_game) self._game_packet = self._entity_packet self.current_block.packets.append(self._entity_packet) def block_start(self, ts, entity, type, index, effectid, effectindex, target): entity = self.parse_entity(entity) type = parse_enum(enums.BlockType, type) if index is not None: index = int(index) target = self.parse_entity(target) block = packets.Block(ts, entity, type, index, effectid, effectindex, target) block.parent = self.current_block self.current_block.packets.append(block) self.current_block = block def block_end(self, ts): if not self.current_block.parent: logging.warning("[%s] Orphaned BLOCK_END detected" % (ts)) return self.current_block self.current_block.end() block = self.current_block self.current_block = self.current_block.parent return block def full_entity(self, ts, id, cardid): id = int(id) entity = Card(id, cardid) self.current_game.register_entity(entity) self._entity_node = entity self._entity_packet = packets.FullEntity(ts, entity, cardid) self.current_block.packets.append(self._entity_packet) def full_entity_update(self, ts, entity, cardid): id = self.parse_entity_id(entity) return self.full_entity(ts, id, cardid) def show_entity(self, ts, entity, cardid): entity = self.parse_entity(entity) entity.reveal(cardid) self._entity_node = entity self._entity_packet = packets.ShowEntity(ts, entity, cardid) self.current_block.packets.append(self._entity_packet) def hide_entity(self, ts, entity, tag, value): entity = self.parse_entity(entity) entity.hide() tag, value = parse_tag(tag, value) assert tag == GameTag.ZONE packet = packets.HideEntity(ts, entity, value) self.current_block.packets.append(packet) def change_entity(self, ts, entity, cardid): entity = self.parse_entity(entity) entity.change(cardid) self._entity_node = entity self._entity_packet = packets.ChangeEntity(ts, entity, cardid) self.current_block.packets.append(self._entity_packet) def meta_data(self, ts, meta, data, info): meta = parse_enum(enums.MetaDataType, meta) if meta == enums.MetaDataType.JOUST: data = self.parse_entity(data) count = int(info) self._metadata_node = packets.MetaData(ts, meta, data, count) self.current_block.packets.append(self._metadata_node) def tag_change(self, ts, e, tag, value): entity = self.parse_entity(e) tag, value = parse_tag(tag, value) self._check_for_mulligan_hack(ts, entity, tag, value) if not isinstance(entity, Entity): entity = self.check_for_player_registration(tag, value, e) packet = packets.TagChange(ts, entity, tag, value) self.current_block.packets.append(packet) if not entity or not isinstance(entity, Entity): if tag == enums.GameTag.ENTITY_ID: self.register_player_name(self.current_game, e, value) else: self.buffer_packet_entity_update(packet, e) else: entity.tag_change(tag, value) return entity
class PowerHandler(object): def __init__(self): super(PowerHandler, self).__init__() self.current_block = None self._entity_node = None self._metadata_node = None def _check_for_mulligan_hack(self, ts, entity, tag, value): # Old game logs didn't handle asynchronous mulligans properly. # If we're missing an ACTION_END packet after the mulligan SendChoices, # we just close it out manually. if tag == enums.GameTag.MULLIGAN_STATE and value == enums.Mulligan.DEALING: assert self.current_block if isinstance(self.current_block, packets.Block): logging.warning("WARNING: Broken mulligan nesting. Working around...") self.block_end(ts) def find_callback(self, method): if method == self.parse_method("DebugPrintPower"): return self.handle_data def parse_initial_tag(self, data): sre = TAG_VALUE_RE.match(data) tag, value = sre.groups() return parse_tag(tag, value) def handle_data(self, ts, data): opcode = data.split()[0] if opcode in PowerType.__members__: return self.handle_power(ts, opcode, data) if opcode == "GameEntity": self.flush() sre = GAME_ENTITY_RE.match(data) self.register_game(ts, *sre.groups()) elif opcode == "Player": self.flush() sre = PLAYER_ENTITY_RE.match(data) self.register_player(ts, *sre.groups()) elif opcode.startswith("tag="): tag, value = self.parse_initial_tag(data) self._entity_packet.tags.append((tag, value)) elif opcode.startswith("Info["): sre = METADATA_INFO_RE.match(data) idx, entity = sre.groups() entity = self.parse_entity(entity) self._metadata_node.info.append(entity) else: raise NotImplementedError(data) def flush(self): if self._entity_node: for k, v in self._entity_packet.tags: self._entity_node.tags[k] = v self._entity_node = None if self._metadata_node: self._metadata_node = None def handle_power(self, ts, opcode, data): self.flush() if opcode == "CREATE_GAME": regex, callback = CREATE_GAME_RE, self.create_game elif opcode in ("ACTION_START", "BLOCK_START"): sre = BLOCK_START_RE.match(data) if sre is None: sre = ACTION_START_OLD_RE.match(data) entity, type, index, target = sre.groups() effectid, effectindex = None, None else: type, entity, effectid, effectindex, target = sre.groups() index = None self.block_start(ts, entity, type, index, effectid, effectindex, target) return elif opcode in ("ACTION_END", "BLOCK_END"): regex, callback = BLOCK_END_RE, self.block_end elif opcode == "FULL_ENTITY": if data.startswith("FULL_ENTITY - Updating"): regex, callback = FULL_ENTITY_UPDATE_RE, self.full_entity_update else: regex, callback = FULL_ENTITY_CREATE_RE, self.full_entity elif opcode == "SHOW_ENTITY": regex, callback = SHOW_ENTITY_RE, self.show_entity elif opcode == "HIDE_ENTITY": regex, callback = HIDE_ENTITY_RE, self.hide_entity elif opcode == "CHANGE_ENTITY": regex, callback = CHANGE_ENTITY_RE, self.change_entity elif opcode == "TAG_CHANGE": regex, callback = TAG_CHANGE_RE, self.tag_change elif opcode == "META_DATA": regex, callback = META_DATA_RE, self.meta_data else: raise NotImplementedError(data) sre = regex.match(data) if not sre: logging.warning("Could not correctly parse %r", data) return callback(ts, *sre.groups()) # Messages def create_game(self, ts): self.current_game = Game(0) packet_tree = packets.PacketTree(ts) packet_tree.spectator_mode = self.spectator_mode packet_tree.game = self.current_game self.games.append(packet_tree) self.current_block = packet_tree self._entity_packet = packets.CreateGame(ts, self.current_game) self._game_packet = self._entity_packet self.current_block.packets.append(self._entity_packet) def block_start(self, ts, entity, type, index, effectid, effectindex, target): entity = self.parse_entity(entity) type = parse_enum(enums.BlockType, type) if index is not None: index = int(index) target = self.parse_entity(target) block = packets.Block(ts, entity, type, index, effectid, effectindex, target) block.parent = self.current_block self.current_block.packets.append(block) self.current_block = block def block_end(self, ts): if not self.current_block.parent: logging.warning("[%s] Orphaned BLOCK_END detected" % (ts)) return self.current_block self.current_block.end() block = self.current_block self.current_block = self.current_block.parent return block def full_entity(self, ts, id, cardid): id = int(id) entity = Card(id, cardid) self.current_game.register_entity(entity) self._entity_node = entity self._entity_packet = packets.FullEntity(ts, entity, cardid) self.current_block.packets.append(self._entity_packet) def full_entity_update(self, ts, entity, cardid): id = self.parse_entity_id(entity) return self.full_entity(ts, id, cardid) def show_entity(self, ts, entity, cardid): entity = self.parse_entity(entity) entity.reveal(cardid) self._entity_node = entity self._entity_packet = packets.ShowEntity(ts, entity, cardid) self.current_block.packets.append(self._entity_packet) def hide_entity(self, ts, entity, tag, value): entity = self.parse_entity(entity) entity.hide() tag, value = parse_tag(tag, value) assert tag == GameTag.ZONE packet = packets.HideEntity(ts, entity, value) self.current_block.packets.append(packet) def change_entity(self, ts, entity, cardid): entity = self.parse_entity(entity) entity.change(cardid) self._entity_node = entity self._entity_packet = packets.ChangeEntity(ts, entity, cardid) self.current_block.packets.append(self._entity_packet) def meta_data(self, ts, meta, data, info): meta = parse_enum(enums.MetaDataType, meta) if meta == enums.MetaDataType.JOUST: data = self.parse_entity(data) count = int(info) self._metadata_node = packets.MetaData(ts, meta, data, count) self.current_block.packets.append(self._metadata_node) def tag_change(self, ts, e, tag, value): entity = self.parse_entity(e) tag, value = parse_tag(tag, value) self._check_for_mulligan_hack(ts, entity, tag, value) if not isinstance(entity, Entity): entity = self.check_for_player_registration(tag, value, e) packet = packets.TagChange(ts, entity, tag, value) self.current_block.packets.append(packet) if not entity or not isinstance(entity, Entity): if tag == enums.GameTag.ENTITY_ID: self.register_player_name(self.current_game, e, value) else: self.buffer_packet_entity_update(packet, e) else: entity.tag_change(tag, value) return entity