def handleInitGame(self, event, replay): # without tracker events game heart games can't be fixed if len(replay.tracker_events) == 0: yield PluginExit(self, code=0, details=dict()) return start_frame = -1 actual_players = {} for event in replay.tracker_events: if start_frame != -1 and event.frame > start_frame + 5: # fuzz it a little break if event.name == 'UnitBornEvent' and event.control_pid and event.unit_type_name in self.PRIMARY_BUILDINGS: # In normal replays, starting units are born on frame zero. if event.frame == 0: yield PluginExit(self, code=0, details=dict()) return else: start_frame = event.frame actual_players[event.control_pid] = self.PRIMARY_BUILDINGS[ event.unit_type_name] self.fix_entities(replay, actual_players) self.fix_events(replay, start_frame) replay.frames -= start_frame replay.game_length = Length(seconds=replay.frames / 16) replay.real_type = get_real_type(replay.teams) replay.real_length = Length( seconds=int(replay.game_length.seconds / GAME_SPEED_FACTOR[replay.speed])) replay.start_time = datetime.utcfromtimestamp( replay.unix_timestamp - replay.real_length.seconds)
def handleInitGame(self, event, replay): # without tracker events game heart games can't be fixed if len(replay.tracker_events) == 0: yield PluginExit(self, code=0, details=dict()) return start_frame = -1 actual_players = {} for event in replay.tracker_events: if start_frame != -1 and event.frame > start_frame + 5: # fuzz it a little break if event.name == 'UnitBornEvent' and event.control_pid and event.unit_type_name in self.PRIMARY_BUILDINGS: # In normal replays, starting units are born on frame zero. if event.frame == 0: yield PluginExit(self, code=0, details=dict()) return else: start_frame = event.frame actual_players[event.control_pid] = self.PRIMARY_BUILDINGS[event.unit_type_name] self.fix_entities(replay, actual_players) self.fix_events(replay, start_frame) replay.frames -= start_frame replay.game_length = Length(seconds=replay.frames / 16) replay.real_type = get_real_type(replay.teams) replay.real_length = Length(seconds=int(replay.game_length.seconds/GAME_SPEED_FACTOR[replay.speed])) replay.start_time = datetime.utcfromtimestamp(replay.unix_timestamp-replay.real_length.seconds)
def __init__(self, summary_file, filename=None, lang='enUS', **options): super(GameSummary, self).__init__(summary_file, filename, lang=lang, **options) #: A dict of team# -> teams self.team = dict() #: A list of teams self.teams = list() #: Players, a dict of :class`PlayerSummary` from the game self.players = list() self.observers = list() #: Game start and end times self.start_time = None self.end_time = None self.winners = list() self.player = dict() self.settings = dict() self.player_stats = dict() self.player_settings = defaultdict(dict) self.build_orders = defaultdict(list) self.image_urls = list() self.localization_urls = dict() self.lobby_properties = dict() self.lobby_player_properties = dict() self.game_type = str() self.real_type = str() # The first 16 bytes appear to be some sort of compression header buffer = BitPackedDecoder(zlib.decompress(summary_file.read()[16:])) # TODO: Is there a fixed number of entries? # TODO: Maybe the # of parts is recorded somewhere? self.parts = list() while not buffer.done(): self.parts.append(buffer.read_struct()) self.end_time = datetime.utcfromtimestamp(self.parts[0][8]) self.game_speed = LOBBY_PROPERTIES[0xBB8][1][self.parts[0][0] [1].decode('utf8')] self.game_length = utils.Length(seconds=self.parts[0][7]) self.real_length = utils.Length( seconds=int(self.parts[0][7] / GAME_SPEED_FACTOR[self.game_speed])) self.start_time = datetime.utcfromtimestamp(self.parts[0][8] - self.real_length.seconds) self.load_translations() self.load_map_info() self.load_settings() self.load_player_stats() self.load_players() dependencies = [sheet[1] for sheet in self.lang_sheets['enUS']] if 'Swarm (Mod)' in dependencies: self.expansion = 'HotS' elif 'Liberty (Mod)' in dependencies: self.expansion = 'WoL' else: self.expansion = '' self.game_type = self.settings['Teams'].replace(" ", "") self.real_type = utils.get_real_type(self.teams) # The s2gs file also keeps reference to a series of s2mv files # Some of these appear to be encoded bytes and others appear to be # the preview images that authors may bundle with their maps. self.s2mv_urls = [ str(utils.DepotFile(file_hash)) for file_hash in self.parts[0][6][7] ]
def load_players(self): #If we don't at least have details and attributes_events we can go no further if 'replay.details' not in self.raw_data: return if 'replay.attributes.events' not in self.raw_data: return if 'replay.initData' not in self.raw_data: return self.clients = list() self.client = dict() # For players, we can use the details file to look up additional # information. detail_id marks the current index into this data. detail_id = 0 player_id = 1 details = self.raw_data['replay.details'] initData = self.raw_data['replay.initData'] # Assume that the first X map slots starting at 1 are player slots # so that we can assign player ids without the map self.entities = list() for slot_id, slot_data in enumerate(initData['lobby_state']['slots']): user_id = slot_data['user_id'] if slot_data['control'] == 2: if slot_data['observe'] == 0: self.entities.append( Participant(slot_id, slot_data, user_id, initData['user_initial_data'][user_id], player_id, details['players'][detail_id], self.attributes.get(player_id, dict()))) detail_id += 1 player_id += 1 else: self.entities.append( Observer(slot_id, slot_data, user_id, initData['user_initial_data'][user_id], player_id)) player_id += 1 elif slot_data['control'] == 3: self.entities.append( Computer(slot_id, slot_data, player_id, details['players'][detail_id], self.attributes.get(player_id, dict()))) detail_id += 1 player_id += 1 def get_team(team_id): if team_id is not None and team_id not in self.team: team = Team(team_id) self.team[team_id] = team self.teams.append(team) return self.team[team_id] # Set up all our cross reference data structures for entity in self.entities: if entity.is_observer is False: entity.team = get_team(entity.team_id) entity.team.players.append(entity) self.players.append(entity) self.player[entity.pid] = entity else: self.observers.append(entity) self.observer[entity.uid] = entity if entity.is_human: self.humans.append(entity) self.human[entity.uid] = entity else: self.computers.append(entity) self.computer[entity.pid] = entity # Index by pid so that we can match events to players in pre-HotS replays self.entity[entity.pid] = entity # Pull results up for teams for team in self.teams: results = set([p.result for p in team.players]) if len(results) == 1: team.result = list(results)[0] if team.result == 'Win': self.winner = team else: self.logger.warn( "Conflicting results for Team {0}: {1}".format( team.number, results)) team.result = 'Unknown' self.teams.sort(key=lambda t: t.number) # These are all deprecated self.clients = self.humans self.people = self.entities self.client = self.human self.person = self.entity self.real_type = utils.get_real_type(self.teams) # Assign the default region to computer players for consistency # We know there will be a default region because there must be # at least 1 human player or we wouldn't have a replay. default_region = self.humans[0].region for entity in self.entities: if not entity.region: entity.region = default_region # Pretty sure this just never worked, forget about it for now self.recorder = None entity_names = sorted(map(lambda p: p.name, self.entities)) hash_input = self.gateway + ":" + ','.join(entity_names) self.people_hash = hashlib.sha256( hash_input.encode('utf8')).hexdigest() # The presence of observers and/or computer players makes this not actually ladder # This became an issue in HotS where Training, vs AI, Unranked, and Ranked # were all marked with "amm" => Ladder if len(self.observers) > 0 or len(self.humans) != len(self.players): self.is_ladder = False
def __init__(self, summary_file, filename=None, lang='enUS', **options): super(GameSummary, self).__init__(summary_file, filename, lang=lang, **options) #: A dict of team# -> teams self.team = dict() #: A list of teams self.teams = list() #: Players, a dict of :class`PlayerSummary` from the game self.players = list() self.observers = list() #: Game start and end times self.start_time = None self.end_time = None self.winners = list() self.player = dict() self.settings = dict() self.player_stats = dict() self.player_settings = defaultdict(dict) self.build_orders = defaultdict(list) self.image_urls = list() self.localization_urls = dict() self.lobby_properties = dict() self.lobby_player_properties = dict() self.game_type = str() self.real_type = str() # The first 16 bytes appear to be some sort of compression header buffer = BitPackedDecoder(zlib.decompress(summary_file.read()[16:])) # TODO: Is there a fixed number of entries? # TODO: Maybe the # of parts is recorded somewhere? self.parts = list() while not buffer.done(): self.parts.append(buffer.read_struct()) self.end_time = datetime.utcfromtimestamp(self.parts[0][8]) self.game_speed = LOBBY_PROPERTIES[0xBB8][1][self.parts[0][0][1].decode('utf8')] self.game_length = utils.Length(seconds=self.parts[0][7]) self.real_length = utils.Length(seconds=int(self.parts[0][7]/GAME_SPEED_FACTOR[self.game_speed])) self.start_time = datetime.utcfromtimestamp(self.parts[0][8] - self.real_length.seconds) self.load_translations() self.load_map_info() self.load_settings() self.load_player_stats() self.load_players() dependencies = [sheet[1] for sheet in self.lang_sheets['enUS']] if 'Swarm (Mod)' in dependencies: self.expansion = 'HotS' elif 'Liberty (Mod)' in dependencies: self.expansion = 'WoL' else: self.expansion = '' self.game_type = self.settings['Teams'].replace(" ", "") self.real_type = utils.get_real_type(self.teams) # The s2gs file also keeps reference to a series of s2mv files # Some of these appear to be encoded bytes and others appear to be # the preview images that authors may bundle with their maps. self.s2mv_urls = [str(utils.DepotFile(file_hash)) for file_hash in self.parts[0][6][7]]
def load_players(self): # If we don't at least have details and attributes_events we can go no further if 'replay.details' not in self.raw_data: return if 'replay.attributes.events' not in self.raw_data: return if 'replay.initData' not in self.raw_data: return self.clients = list() self.client = dict() # For players, we can use the details file to look up additional # information. detail_id marks the current index into this data. detail_id = 0 player_id = 1 details = self.raw_data['replay.details'] initData = self.raw_data['replay.initData'] # Assume that the first X map slots starting at 1 are player slots # so that we can assign player ids without the map self.entities = list() for slot_id, slot_data in enumerate(initData['lobby_state']['slots']): user_id = slot_data['user_id'] if slot_data['control'] == 2: if slot_data['observe'] == 0: self.entities.append(Participant(slot_id, slot_data, user_id, initData['user_initial_data'][user_id], player_id, details['players'][detail_id], self.attributes.get(player_id, dict()))) detail_id += 1 player_id += 1 else: self.entities.append(Observer(slot_id, slot_data, user_id, initData['user_initial_data'][user_id], player_id)) player_id += 1 elif slot_data['control'] == 3: self.entities.append(Computer(slot_id, slot_data, player_id, details['players'][detail_id], self.attributes.get(player_id, dict()))) detail_id += 1 player_id += 1 def get_team(team_id): if team_id is not None and team_id not in self.team: team = Team(team_id) self.team[team_id] = team self.teams.append(team) return self.team[team_id] # Set up all our cross reference data structures for entity in self.entities: if entity.is_observer is False: entity.team = get_team(entity.team_id) entity.team.players.append(entity) self.players.append(entity) self.player[entity.pid] = entity else: self.observers.append(entity) self.observer[entity.uid] = entity if entity.is_human: self.humans.append(entity) self.human[entity.uid] = entity else: self.computers.append(entity) self.computer[entity.pid] = entity # Index by pid so that we can match events to players in pre-HotS replays self.entity[entity.pid] = entity # Pull results up for teams for team in self.teams: results = set([p.result for p in team.players]) if len(results) == 1: team.result = list(results)[0] if team.result == 'Win': self.winner = team else: self.logger.warn("Conflicting results for Team {0}: {1}".format(team.number, results)) team.result = 'Unknown' self.teams.sort(key=lambda t: t.number) # These are all deprecated self.clients = self.humans self.people = self.entities self.client = self.human self.person = self.entity self.real_type = utils.get_real_type(self.teams) # Assign the default region to computer players for consistency # We know there will be a default region because there must be # at least 1 human player or we wouldn't have a replay. default_region = self.humans[0].region for entity in self.entities: if not entity.region: entity.region = default_region # Pretty sure this just never worked, forget about it for now self.recorder = None entity_names = sorted(map(lambda p: p.name, self.entities)) hash_input = self.region+":"+','.join(entity_names) self.people_hash = hashlib.sha256(hash_input.encode('utf8')).hexdigest() # The presence of observers and/or computer players makes this not actually ladder # This became an issue in HotS where Training, vs AI, Unranked, and Ranked # were all marked with "amm" => Ladder if len(self.observers) > 0 or len(self.humans) != len(self.players): self.is_ladder = False