def get_dataset_data(header): """Get dataset.""" sample = header.initial.players[0].attributes.player_stats mod = None if 'mod' in sample: mod = (sample.mod.get('id'), sample.mod.get('version')) _, ref = get_dataset(header.version, mod) if header.version == Version.DE: return { 'id': 100, 'name': 'Definitive Edition', 'version': None }, ref elif header.version == Version.HD: return { 'id': 300, 'name': 'HD Edition', 'version': resolve_hd_version(header.hd, header.save_version) }, ref if 'mod' in sample and sample.mod['id'] == 0 and sample.mod['version'] == '2': raise ValueError("invalid mod version") if 'mod' in sample and sample.mod['id'] > 0: return sample.mod, ref if 'trickle_food' in sample and sample.trickle_food: return { 'id': 1, 'name': mgz.const.MODS.get(1), 'version': '<5.7.2' }, ref if header.version == Version.AOK: return { 'id': 200, 'name': 'Age of Kings', 'version': '2.0a' }, ref if header.version == Version.AOC10: return { 'id': 0, 'name': 'The Conquerors', 'version': '1.0' }, ref return { 'id': 0, 'name': 'The Conquerors', 'version': '1.0c' }, ref
def parse_match(handle): """Parse a match. This is one big function because the dependency graph between the variables is dense. """ data = parse(handle) body_pos = handle.tell() - 4 # log version consts = get_consts() dataset_id, dataset = get_dataset(data['version'], data['mod']) map_id = data['hd']['map_id'] if data['version'] is Version.HD else data[ 'scenario']['map_id'] try: map_data, encoding, language = get_map_data( map_id, data['scenario']['instructions'], data['map']['dimension'], data['version'], dataset_id, dataset, data['map']['tiles'], de_seed=data['lobby']['seed']) except ValueError: raise RuntimeError("could not get map data") # Handle DE-specific data if data['de']: de_players = { player['number']: player for player in data['de']['players'] } lobby = data['de']['lobby'] guid = data['de']['guid'] else: de_players = dict() lobby = None guid = None # Parse gaia objects gaia = [ Object(dataset['objects'].get(str(obj['object_id'])), obj['class_id'], obj['object_id'], obj['instance_id'], obj['index'], Position(obj['position']['x'], obj['position']['y'])) for obj in data['players'][0]['objects'] ] inputs = Inputs({o.instance_id: o.name for o in gaia}) # Parse players players = dict() allies = dict() for player in data['players'][1:]: allies[player['number']] = set([player['number']]) for i, stance in enumerate(player['diplomacy']): if stance == 2: allies[player['number']].add(i) de_player = de_players.get(player['number']) if de_player: player.update(de_player) pos_x = None pos_y = None for obj in player['objects']: if obj['object_id'] in TC_IDS: pos_x = obj['position']['x'] pos_y = obj['position']['y'] players[player['number']] = Player( player['number'], player['name'].decode(encoding), consts['player_colors'][str(player['color_id'])], player['color_id'], dataset['civilizations'][str(player['civilization_id'])]['name'], player['civilization_id'], Position(pos_x, pos_y), [ Object(dataset['objects'].get(str( obj['object_id'])), obj['class_id'], obj['object_id'], obj['instance_id'], obj['index'], Position(obj['position']['x'], obj['position']['y'])) for obj in player['objects'] ], player.get('profile_id'), player.get('prefer_random')) # Assign teams if de_players: by_team = collections.defaultdict(list) for number, player in de_players.items(): if player['team_id'] > 1: by_team[player['team_id']].append(number) elif player['team_id'] == 1: by_team[number + 9].append(number) team_ids = by_team.values() else: team_ids = set([frozenset(s) for s in allies.values()]) teams = [] for team in team_ids: t = [players[x] for x in team] for x in team: players[x].team = t teams.append(t) # Compute diplomacy diplomacy_type = get_diplomacy_type(teams, players) # Extract lobby chat pd = [dict(name=p.name, number=n) for n, p in players.items()] chats = [] for c in data['lobby']['chat']: chat = parse_chat(c, encoding, 0, pd, diplomacy_type, 'lobby') if chat['type'] == ChatEnum.DISCARD or chat[ 'player_number'] not in players: continue chats.append( Chat(timedelta(milliseconds=chat['timestamp']), chat['message'], chat['origination'], chat['audience'], players[chat['player_number']])) inputs.add_chat(chats[-1]) # Parse player actions fast.meta(handle) timestamp = 0 resigned = [] actions = [] viewlocks = [] last_viewlock = None while True: try: op_type, op_data = fast.operation(handle) if op_type is fast.Operation.SYNC: timestamp += op_data[0] elif op_type is fast.Operation.VIEWLOCK: if op_data == last_viewlock: continue viewlock = Viewlock(timedelta(milliseconds=timestamp), Position(*op_data), players[data['metadata']['owner_id']]) viewlocks.append(viewlock) last_viewlock = op_data elif op_type is fast.Operation.CHAT: chat = parse_chat(op_data, encoding, timestamp, pd, diplomacy_type, 'game') if chat['type'] == ChatEnum.MESSAGE: chats.append( Chat( timedelta(milliseconds=chat['timestamp'] + data['map']['restore_time']), chat['message'], chat['origination'], chat['audience'], players[chat['player_number']])) inputs.add_chat(chats[-1]) elif op_type is fast.Operation.ACTION: action_type, action_data = op_data action = Action(timedelta(milliseconds=timestamp), action_type, action_data) if action_type is fast.Action.RESIGN: resigned.append(players[action_data['player_id']]) if 'player_id' in action_data and action_data[ 'player_id'] in players: action.player = players[action_data['player_id']] del action.payload['player_id'] enrich_action(action, action_data, dataset, consts) actions.append(action) inputs.add_action(action) except EOFError: break # Compute winner(s) for team in teams: winner = not any([player for player in team if player in resigned]) if resigned: for player in team: player.winner = winner handle.seek(body_pos) file_bytes = handle.read() file_size = body_pos + 4 + len(file_bytes) file_hash = hashlib.sha1(file_bytes).hexdigest() return Match( list(players.values()), teams, gaia, Map( map_id, map_data['name'], map_data['dimension'], consts['map_sizes'][str(map_data['dimension'])], map_data['custom'], map_data['seed'], data['de']['rms_mod_id'] if data['version'] is Version.DE else None, map_data['name'].startswith('ZR@'), map_data['modes'], [ Tile(tile['terrain_id'], tile['elevation'], Position(tile['x'], tile['y'])) for tile in map_data['tiles'] ]), File(codecs.lookup(encoding), language, file_hash, file_size, players[data['metadata']['owner_id']], viewlocks), data['map']['restore_time'] > 0, timedelta(milliseconds=data['map']['restore_time']), consts['speeds'][str(int(round(data['metadata']['speed'], 2) * 100))], int(round(data['metadata']['speed'], 2) * 100), data['metadata']['cheats'], data['lobby']['lock_teams'], data['lobby']['population'], chats, guid, lobby, dataset['dataset']['name'], consts['game_types'][str( data['lobby']['game_type_id'])], data['lobby']['game_type_id'], consts['map_reveal_choices'][str(data['lobby']['reveal_map_id'])], data['lobby']['reveal_map_id'], consts['difficulties'].get(str(get_difficulty(data))), get_difficulty(data), consts['starting_ages'].get(str(get_starting_age(data))), get_starting_age(data), get_team_together(data), get_lock_speed(data), get_all_technologies(data), True if data['version'] is Version.DE else None, timedelta(milliseconds=timestamp + data['map']['restore_time']), diplomacy_type, bool(resigned), data['version'], data['game_version'], data['save_version'], data['log_version'], data['de']['build'] if data['version'] is Version.DE else None, datetime.fromtimestamp(data['de']['timestamp']) if data['version'] is Version.DE and data['de']['timestamp'] else None, timedelta(seconds=data['de']['spec_delay']) if data['version'] is Version.DE else None, data['de']['allow_specs'] if data['version'] is Version.DE else None, data['de']['hidden_civs'] if data['version'] is Version.DE else None, data['de']['visibility_id'] == 2 if data['version'] is Version.DE else None, get_hash(data), actions, inputs.inputs)
def parse_match(handle): """Parse a match. This is one big function because the dependency graph between the variables is dense. """ data = parse(handle) consts = get_consts() dataset_id, dataset = get_dataset(data['version'], data['mod']) # self._header.hd.selected_map_id if self._header.hd else self._header.scenario.game_settings.map_id map_data, encoding, language = get_map_data( data['hd']['map_id'] if data['version'] is Version.HD else data['scenario']['map_id'], data['scenario']['instructions'], data['map']['dimension'], data['version'], dataset_id, dataset, data['map']['tiles'], de_seed=data['lobby']['seed']) # Handle DE-specific data if data['de']: de_players = { player['number']: player for player in data['de']['players'] } lobby = data['de']['lobby'] guid = data['de']['guid'] else: de_players = dict() lobby = None guid = None # Parse gaia objects gaia = [ Object(dataset['objects'].get(str(obj['object_id'])), obj['instance_id'], Position(obj['position']['x'], obj['position']['y'])) for obj in data['players'][0]['objects'] ] inputs = Inputs({o.instance_id: o.name for o in gaia}) # Parse players players = dict() allies = dict() for player in data['players'][1:]: allies[player['number']] = set([player['number']]) for i, stance in enumerate(player['diplomacy']): if stance == 2: allies[player['number']].add(i) de_player = de_players.get(player['number']) if de_player: player.update(de_player) pos_x = None pos_y = None for obj in player['objects']: if obj['object_id'] in TC_IDS: pos_x = obj['position']['x'] pos_y = obj['position']['y'] players[player['number']] = Player( player['color_id'] + 1, player['name'].decode(encoding), consts['player_colors'][str(player['color_id'])], dataset['civilizations'][str(player['civilization_id'])]['name'], Position(pos_x, pos_y), [ Object(dataset['objects'].get(str(obj['object_id'])), obj['instance_id'], Position(obj['position']['x'], obj['position']['y'])) for obj in player['objects'] ], player.get('profile_id')) # Assign teams team_ids = set([frozenset(s) for s in allies.values()]) teams = [] for team in team_ids: t = [players[x] for x in team] for x in team: players[x].team = t teams.append(t) # Compute diplomacy diplomacy_type = get_diplomacy_type(teams, players) # Extract lobby chat pd = [dict(name=p.name, number=n) for n, p in players.items()] chats = [] for c in data['lobby']['chat']: chat = parse_chat(c, encoding, 0, pd, diplomacy_type, 'lobby') if chat['player_number'] not in players: continue chats.append( Chat(timedelta(milliseconds=chat['timestamp']), chat['message'], players[chat['player_number']])) inputs.add_chat(chats[-1]) # Parse player actions fast.meta(handle) timestamp = 0 resigned = [] actions = [] viewlocks = [] last_viewlock = None while True: try: op_type, op_data = fast.operation(handle) if op_type is fast.Operation.SYNC: timestamp += op_data[0] elif op_type is fast.Operation.VIEWLOCK: if op_data == last_viewlock: continue viewlock = Viewlock(timedelta(milliseconds=timestamp), Position(*op_data), players[data['metadata']['owner_id']]) viewlocks.append(viewlock) last_viewlock = op_data elif op_type is fast.Operation.CHAT: chat = parse_chat(op_data, encoding, timestamp, pd, diplomacy_type, 'game') if chat['type'] == ChatEnum.MESSAGE: chats.append( Chat(timedelta(milliseconds=chat['timestamp']), chat['message'], players[chat['player_number']])) inputs.add_chat(chats[-1]) elif op_type is fast.Operation.ACTION: action_type, action_data = op_data action = Action(timedelta(milliseconds=timestamp), action_type, action_data) if action_type is fast.Action.RESIGN: resigned.append(players[action_data['player_id']]) if 'player_id' in action_data: action.player = players[action_data['player_id']] del action.payload['player_id'] enrich_action(action, action_data, dataset, consts) actions.append(action) inputs.add_action(action) except EOFError: break # Compute winner(s) for team in teams: winner = not any([player for player in team if player in resigned]) for player in team: player.winner = winner return Match( list(players.values()), teams, gaia, Map(map_data['name'], map_data['dimension'], consts['map_sizes'][str(map_data['dimension'])], map_data['custom'], map_data['seed'], [ Tile(tile['terrain_id'], tile['elevation'], Position(tile['x'], tile['y'])) for tile in map_data['tiles'] ]), File(codecs.lookup(encoding), language, players[data['metadata']['owner_id']], viewlocks), consts['speeds'][str(int(round(data['metadata']['speed'], 2) * 100))], data['metadata']['cheats'], data['lobby']['lock_teams'], data['lobby']['population'], chats, guid, lobby, dataset['dataset']['name'], consts['game_types'][str(data['lobby']['game_type_id'])], consts['map_reveal_choices'][str(data['lobby']['reveal_map_id'])], timedelta(milliseconds=timestamp), diplomacy_type, data['version'], actions, inputs.inputs)