def handleUnitDiedEvent(self, event, replay): if not replay.datapack: return if event.unit_id in replay.objects: event.unit = replay.objects[event.unit_id] event.unit.died_at = event.frame event.unit.location = event.location if event.unit_id_index in replay.active_units: del replay.active_units[event.unit_id_index] else: self.logger.error( "Unable to delete unit index {0} at {1} [{2}], index not active." .format(event.killer_pid, Length(seconds=event.second), event.frame)) else: self.logger.error( "Unit {0} died at {1} [{2}] before it was born!".format( event.unit_id, Length(seconds=event.second), event.frame)) if event.killer_pid in replay.player: event.killer = replay.player[event.killer_pid] if event.unit: event.unit.killed_by = event.killer event.killer.killed_units.append(event.unit) elif event.killer_pid: self.logger.error("Unknown killer pid {0} at {1} [{2}]".format( event.killer_pid, Length(seconds=event.second), event.frame))
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 load_message_game_player(self, event, replay): if replay.versions[1] == 1 or (replay.versions[1] == 2 and replay.build < 24247): if event.pid in replay.entity: event.player = replay.entity[event.pid] event.player.events.append(event) elif event.pid != 16: self.logger.error( "Bad pid ({0}) for event {1} at {2} [{3}].".format( event.pid, event.__class__, Length(seconds=event.second), event.frame)) else: pass # This is a global event else: # Now event.pid is actually a user id for human entities if event.pid < len(replay.humans): event.player = replay.human[event.pid] event.player.events.append(event) elif event.pid != 16: self.logger.error( "Bad pid ({0}) for event {1} at {2} [{3}].".format( event.pid, event.__class__, Length(seconds=event.second), event.frames)) else: pass # This is a global event
def load_tracker_controller(self, event, replay): if event.control_pid in replay.entity: event.unit_controller = replay.entity[event.control_pid] elif event.control_pid != 0: self.logger.error( "Bad control_pid ({0}) for event {1} at {2} [{3}].".format( event.control_pid, event.__class__, Length(seconds=event.second), event.frame))
def load_tracker_player(self, event, replay): if event.pid in replay.entity: event.player = replay.entity[event.pid] else: self.logger.error( "Bad pid ({0}) for event {1} at {2} [{3}].".format( event.pid, event.__class__, Length(seconds=event.second), event.frame))
def SelectionTracker(replay): debug = replay.opt['debug'] logger = log_utils.get_logger(SelectionTracker) for person in replay.entities: # TODO: A more robust person interface might be nice person.selection_errors = 0 player_selections = GameState(PlayerSelection()) for event in person.events: error = False if event.name == 'SelectionEvent': selections = player_selections[event.frame] control_group = selections[event.control_group].copy() error = not control_group.deselect(event.mask_type, event.mask_data) control_group.select(event.new_units) selections[event.control_group] = control_group if debug: logger.info("[{0}] {1} selected {2} units: {3}".format(Length(seconds=event.second), person.name, len(selections[0x0A].objects), selections[0x0A])) elif event.name == 'SetControlGroupEvent': selections = player_selections[event.frame] selections[event.control_group] = selections[0x0A].copy() if debug: logger.info("[{0}] {1} set hotkey {2} to current selection".format(Length(seconds=event.second), person.name, event.hotkey)) elif event.name == 'AddToControlGroupEvent': selections = player_selections[event.frame] control_group = selections[event.control_group].copy() error = not control_group.deselect(event.mask_type, event.mask_data) control_group.select(selections[0x0A].objects) selections[event.control_group] = control_group if debug: logger.info("[{0}] {1} added current selection to hotkey {2}".format(Length(seconds=event.second), person.name, event.hotkey)) elif event.name == 'GetControlGroupEvent': selections = player_selections[event.frame] control_group = selections[event.control_group].copy() error = not control_group.deselect(event.mask_type, event.mask_data) selections[0xA] = control_group if debug: logger.info("[{0}] {1} retrieved hotkey {2}, {3} units: {4}".format(Length(seconds=event.second), person.name, event.control_group, len(selections[0x0A].objects), selections[0x0A])) else: continue # TODO: The event level interface here should be improved # Possibly use 'added' and 'removed' unit lists as well event.selected = selections[0x0A].objects if error: person.selection_errors += 1 if debug: logger.warn("Error detected in deselection mode {0}.".format(event.mask_type)) person.selection = player_selections # Not a real lock, so don't change it! person.selection.locked = True return replay
def _str_prefix(self): if getattr(self, 'pid', 16) == 16: player_name = "Global" elif self.player and not self.player.name: player_name = "Player {0} - ({1})".format(self.player.pid, self.player.play_race) elif self.player: player_name = self.player.name else: player_name = "no name" return "{0}\t{1:<15} ".format(Length(seconds=int(self.frame / 16)), player_name)
def __init__(self, replay_file, **options): #Useful references self.opt = AttributeDict(**options) # Some file-like objects may not support filenames. Issue #21 if hasattr(replay_file, 'name'): self.filename = replay_file.name else: self.filename = "Unavailable" #header information self.versions, self.frames = read_header(replay_file) self.build = self.versions[4] self.release_string = "%s.%s.%s.%s" % tuple(self.versions[1:5]) self.seconds = self.frames / 16 self.length = Length(seconds=self.seconds) #default values, filled in during file read self.player_names = list() self.other_people = set() self.speed = "" self.type = "" self.category = "" self.is_ladder = False self.is_private = False self.map = "" self.gateway = "" self.events = list() self.events_by_type = defaultdict(list) self.results = dict() self.teams = list() self.team = dict() self.observers = list() #Unordered list of Observer self.players = list() #Unordered list of Player self.player = PersonDict() self.people = list() #Unordered list of Players+Observers self.humans = list() #Unordered list of Human People self.person = PersonDict() #Maps pid to Player/Observer self.attributes = list() self.messages = list() self.recorder = None # Player object self.winner_known = False self.packets = list() # Set in parsers.DetailParser.load, should we hide this? self.file_time = None # TODO: Test EPOCH differences between MacOsX and Windows # http://en.wikipedia.org/wiki/Epoch_(reference_date) # Notice that Windows and Mac have different EPOCHs, I wonder whether # this is different depending on the OS on which the replay was played. self.date = None # Date when the game was played in local time self.utc_date = None # Date when the game was played in UTC self.objects = {} self.raw = AttributeDict()
def handleUnitTypeChangeEvent(self, event, replay): if not replay.datapack: return if event.unit_id in replay.objects: event.unit = replay.objects[event.unit_id] replay.datapack.change_type(event.unit, event.unit_type_name, event.frame) else: self.logger.error( "Unit {0} type changed at {1} [{2}] before it was born!". format(event.unit_id, Length(seconds=event.second)))
def load_context(self, replay): if replay.versions[1] == 1 or (replay.versions[1] == 2 and replay.build < 24247): if self.pid <= len(replay.people): self.player = replay.person[self.pid] self.player.events.append(self) elif self.pid != 16: self.logger.error("Bad pid ({0}) for event {1} at {2}.".format( self.pid, self.__class__, Length(seconds=self.second))) else: pass # This is a global event else: if self.pid < len(replay.clients): self.player = replay.client[self.pid] self.player.events.append(self) elif self.pid != 16: self.logger.error("Bad pid ({0}) for event {1} at {2}.".format( self.pid, self.__class__, Length(seconds=self.second))) else: pass # This is a global event
def handleUnitDoneEvent(self, event, replay): if not replay.datapack: return if event.unit_id in replay.objects: event.unit = replay.objects[event.unit_id] event.unit.finished_at = event.frame else: self.logger.error( "Unit {0} done at {1} [{2}] before it was started!".format( event.killer_pid, Length(seconds=event.second), event.frame))
def __init__(self): self.initialize_txt() # Config # Tracked Player name = input("Type the player name you want to track \n") self.tracked_player = name # Tracked Race race = input( "Type the race you want to track (Zerg, Protoss, Terran) \n") self.tracked_race = race if platform == "win32": try: self.replay_folder = f"{expanduser('~')}\\documents\\StarCraft II" except ValueError: print( "Replay folder not found, are you sure Starcraft II is installed?" ) elif platform == "linux" or platform == "linux2": try: self.replay_folder = os.environ['SC2_REPLAYS'] except KeyError: print( "Replay folder not found, are you sure Starcraft II is installed?" ) self.replay_folder = input( "Provide path to your SC2 replay folder. " "This is usually located under .wine \n") # Folder polling rate in seconds self.poll_rate = 5 # Minimum length of a game self.min_length = Length(seconds=60) print("GGParser initialized") print("Tracking folder: " + self.replay_folder) print("Tracking player: " + self.tracked_player + " - " + self.tracked_race) print("Polling the folder every " + str(self.poll_rate) + " seconds") self.zerg = "Zerg" self.protoss = "Protoss" self.terran = "Terran" self.XvZ = Score() self.XvP = Score() self.XvT = Score() self.XvR = Score() # Variables self.replay_path = self.get_latest_replay_init()
def handleUnitPositionsEvent(self, event, replay): if not replay.datapack: return for unit_index, (x, y) in event.positions: if unit_index in replay.active_units: unit = replay.active_units[unit_index] unit.location = (x, y) event.units[unit] = unit.location else: self.logger.error( "Unit at active_unit index {0} moved at {1} [{2}] but it doesn't exist!" .format(event.killer_pid, Length(seconds=event.second), event.frame))
def handleUnitOwnerChangeEvent(self, event, replay): self.load_tracker_controller(event, replay) self.load_tracker_upkeeper(event, replay) if not replay.datapack: return if event.unit_id in replay.objects: event.unit = replay.objects[event.unit_id] else: self.logger.error( "Unit {0} owner changed at {1} [{2}] before it was born!". format(event.unit_id, Length(seconds=event.second), event.frame)) if event.unit_upkeeper: if event.unit.owner: event.unit.owner.units.remove(event.unit) event.unit.owner = event.unit_upkeeper event.unit_upkeeper.units.append(event.unit)
def __init__(self, framestamp, sender, target, text): self.framestamp, self.sender, self.target, self.text = framestamp, sender, target, text self.time = Length(seconds=self.framestamp / 16) self.to_all = (self.target == 0) self.to_allies = (self.target == 2)
def _str_prefix(self): return "{0}\t ".format(Length(seconds=int(self.frame / 16)))
def _str_prefix(self): if self.player: player_name = self.player.name if getattr(self, 'pid', 16) != 16 else "Global" else: player_name = "no name" return "{0}\t{1:<15} ".format(Length(seconds=int(self.frame / 16)), player_name)
def SelectionTracker(replay): debug = replay.opt.debug logger = log_utils.get_logger(SelectionTracker) for person in replay.people: # TODO: A more robust person interface might be nice person.selection_errors = 0 player_selection = GameState(PlayerSelection()) for event in person.selection_events: if debug: logger.debug("Event bytes: " + event.bytes.encode("hex")) error = False selection = player_selection[event.frame] if isinstance(event, SelectionEvent): selection[event.bank] = selection[event.bank].copy() error = not selection[event.bank].deselect(*event.deselect) selection[event.bank].select(event.objects) if debug: logger.info("[{0}] {1} selected {2} units: {3}".format( Length(seconds=event.second), person.name, len(selection[0x0A].objects), selection[0x0A])) elif isinstance(event, GetFromHotkeyEvent): # For some reason they leave the hotkey buffer unmodified so make a copy selection[0x0A] = selection[event.hotkey].copy() error = not selection[0x0A].deselect(*event.deselect) if debug: logger.info( "[{0}] {1} retrieved hotkey {2}, {3} units: {4}". format(Length(seconds=event.second), person.name, event.hotkey, len(selection[0x0A].objects), selection[0x0A])) elif isinstance(event, SetToHotkeyEvent): # Make a copy to decouple the hotkey from primary selection selection[event.hotkey] = selection[0x0A].copy() if debug: logger.info( "[{0}] {1} set hotkey {2} to current selection".format( Length(seconds=event.second), person.name, event.hotkey)) elif isinstance(event, AddToHotkeyEvent): selection[event.hotkey] = selection[event.hotkey].copy() error = not selection[event.hotkey].deselect(*event.deselect) selection[event.hotkey].select(selection[0x0A].objects) if debug: logger.info( "[{0}] {1} added current selection to hotkey {2}". format(Length(seconds=event.second), person.name, event.hotkey)) # TODO: The event level interface here should be improved # Possibly use 'added' and 'removed' unit lists as well event.selected = selection[0x0A].objects if error: person.selection_errors += 1 if debug: logger.warn( "Error detected in deselection mode {0}.".format( event.deselect[0])) person.selection = player_selection # Not a real lock, so don't change it! person.selection.locked = True return replay
def _str_prefix(self): player_name = self.player.name if getattr(self, 'pid', 16) != 16 else "Global" return "%s\t%-15s " % (Length(seconds=int(self.frame / 16)), player_name)
def _str_prefix(self): player_name = self.player.name if getattr(self, "pid", 16) != 16 else "Global" return "{0}\t{1:<15} ".format(Length(seconds=int(self.frame / 22.4)), player_name)
def _str_prefix(self): player_name = self.player.name if self.is_local else "Global" return "%s\t%-15s " % (Length(seconds=int(self.frame / 16)), player_name)