# -*- coding: utf-8 -*- """ Primary Packets for Combat.Log Files. This is the most important part for dealing with actual statistics, since every action taken in a combat instance gets logged here. ------------------------------------ Note: All logs start with something like 23:53:29.137 | LOGDATA LOGDATA can be quite different depending on the logfile. other forms encountered: 23:54:00.600 WARNING| combat logs: 01:04:38.805 CMBT | """ import re from base import Log, L_CMBT, Stacktrace import logging class CombatLog(Log): __slots__ = Log.__slots__ + [ '_match_id', 'values'] @classmethod def _log_handler(cls, log): if log.get('log', '').strip().startswith(cls.__name__): return True return False @classmethod def is_handler(cls, log): if log.get('logtype', None) == L_CMBT: return cls._log_handler(log) return False def __init__(self, values=None): self.values = values or {} self.reviewed = False def unpack(self, force=False): if self.reviewed and not force: return True self._match_id = None # unpacks the data from the values. if hasattr(self, 'matcher') and self.matcher: matchers = self.matcher if not isinstance(matchers, list): matchers = [matchers,] for i, matcher in enumerate(matchers): m = matcher.match(self.values.get('log', '')) if m: self.values.update(m.groupdict()) self._match_id = i self.reviewed = True return True # unknown? if not isinstance(self, UserEvent): logging.warning('Unknown Packet for %s: "%s"' % (self.__class__.__name__, self.values.get('log', ''))) # trash if unknown or no matcher. self.trash = True def explain(self): ''' returns a String readable by humans explaining this Log ''' return self.values.get('log', 'Unknown Combat Log') def clean(self): if 'log' in self.values.keys(): del self.values['log'] # @todo: where does this come from? class Action(CombatLog): __slots__ = CombatLog.__slots__ pass class Gameplay(CombatLog): __slots__ = CombatLog.__slots__ matcher = [ # usual: team(reason). explained reason. re.compile(r"^Gameplay\sfinished\.\sWinner\steam\:\s+(?P\d+)\((?P\w+)\)\.\sFinish\sreason\:\s'(?P[^']+)'\.\sActual\sgame\stime\s+(?P\d+|\d+\.\d+)\ssec"), # team, unexplained reason (unknown, Timeout) re.compile(r"^Gameplay\sfinished\.\sWinner\steam\:\s+(?P\d+).\sFinish\sreason\:\s'(?P[^']+)'\.\sActual\sgame\stime\s+(?P\d+|\d+\.\d+)\ssec"), ] class Apply(CombatLog): # Apply Aura. __slots__ = CombatLog.__slots__ matcher = re.compile(r"^Apply\saura\s'(?P\w+)'\sid\s(?P\d+)\stype\s(?P\w+)\sto\s'(?P[^\']+)'") class Damage(CombatLog): __slots__ = CombatLog.__slots__ matcher = re.compile(r"^Damage\s+(?P[^\s]+)\s\->\s+(?P[^\s]+)\s+(?P(?:\d+|\d+\.\d+))(?:\s(?P[^\s]+)\s|\s{2,2})(?P(?:\w|\|)+)") class Spawn(CombatLog): __slots__ = CombatLog.__slots__ matcher = re.compile(r"^Spawn\sSpaceShip\sfor\splayer(?P\d+)\s\((?P[^,]+),\s+(?P#\w+)\)\.\s+'(?P\w+)'") class Spell(CombatLog): __slots__ = CombatLog.__slots__ matcher = re.compile(r"^Spell\s'(?P\w+)'\sby\s+(?P.*)(?:\((?P\w+)\)|)\stargets\((?P\d+)\)\:(?:\s(?P.+)|\s*)") class Reward(CombatLog): __slots__ = CombatLog.__slots__ matcher = [ # ordinary reward: re.compile(r"^Reward\s+(?P[^\s]+)(?:\s(?P\w+)\s+|\s+)(?P\d+)\s(?P.*)\s+for\s(?P.*)"), # openspace reward (karma): re.compile(r"^Reward\s+(?P[^\s]+)(?:\s(?P\w+)\s+|\s+)\s+(?P[\+\-]\d+)\skarma\spoints\s+for\s(?P.*)"), ] class Participant(CombatLog): __slots__ = CombatLog.__slots__ matcher = re.compile(r"^\s+Participant\s+(?P[^\s]+)(?:\s{2}(?P\w+)|\s{30,})\s+(?:totalDamage\s(?P(?:\d+|\d+\.\d+));\smostDamageWith\s'(?P[^']+)';(?P.*)|<(?P\w+)>)") class Rocket(CombatLog): __slots__ = CombatLog.__slots__ matcher = re.compile(r"^Rocket\s(?Plaunch|detonation)\.\sowner\s'(?P[^']+)'(?:,\s(?:def\s'(?P\w+)'|target\s'(?P[^']+)'|reason\s'(?P\w+)'|directHit\s'(?P[^']+)'))+") class Heal(CombatLog): __slots__ = CombatLog.__slots__ matcher = [ # heal by module re.compile(r"^Heal\s+(?P[^\s]+)\s\->\s+(?P[^\s]+)\s+(?P(?:\d+|\d+\.\d+))\s(?P[^\s]+)"), # direct heal by source or n/a (global buff) re.compile(r"^Heal\s+(?:n/a|(?P\w+))\s+\->\s+(?P[^\s]+)\s+(?P(?:\d+|\d+\.\d+))"), ] class Killed(CombatLog): __slots__ = CombatLog.__slots__ matcher = [ re.compile(r"^Killed\s(?P[^\s]+)\s+(?P\w+);\s+killer\s(?P[^\s]+)\s*"), re.compile(r"^Killed\s(?P[^\(]+)\((?P\w+)\);\s+killer\s(?P[^\s]+)\s*"), re.compile(r"^Killed\s(?P[^\;]+);\s+killer\s(?P[^\s]+)\s+.*"), ] class Captured(CombatLog): __slots__ = CombatLog.__slots__ matcher = re.compile(r"^Captured\s'(?P[^']+)'\(team\s(?P\d+)\)\.(?:\sAttackers\:(?P.*)|.*)") class AddStack(CombatLog): __slots__ = CombatLog.__slots__ matcher = re.compile(r"^AddStack\saura\s'(?P\w+)'\sid\s(?P\d+)\stype\s(?P\w+)\.\snew\sstacks\scount\s(?P\d+)") class Cancel(CombatLog): __slots__ = CombatLog.__slots__ matcher = re.compile(r"^Cancel\saura\s'(?P\w+)'\sid\s(?P\d+)\stype\s(?P\w+)\sfrom\s'(?P[^']*)'") class Scores(CombatLog): __slots__ = CombatLog.__slots__ matcher = re.compile(r"^Scores\s+-\sTeam1\((?P(?:\d+|\d+\.\d+))\)\sTeam2\((?P(?:\d+|\d+\.\d+))\)") class Uncaptured(CombatLog): """ Uncaptured 'VitalPoint_Beacon3_RC'(team 1). Attackers: bergg 10101 Uncaptured 'VitalPoint_Beacon2_RC'(team 1). Attackers: kuja cmdrey Uncaptured 'VitalPoint_Beacon1_RC'(team 2). Attackers: UnknownAgent Uncaptured 'VitalPoint_Beacon1_RC_King'(team 2). Attackers: OregyenDuero CaptainX11 g4borg vacknishkara tatsar46359 (bot)Nicholas (bot)Helen Uncaptured 'VitalPoint_Beacon1_RC_King'(team 2). Attackers: g4borg (bot)Nicholas Uncaptured 'VitalPoint_Beacon3_RC_King'(team 2). Attackers: g4borg Uncaptured 'VitalPoint_Beacon2_SS'(team 2). Attackers: CaptainX11 g4borg Targeht Dvorkin Uncaptured 'VitalPoint_Beacon3_RC'(team 1). Attackers: mnsMonty Uncaptured 'VitalPoint_Beacon1_RC'(team 1). Attackers: OregyenDuero g4borg manbearpig10261 Uncaptured 'VitalPoint_Beacon2_RC'(team 2). Attackers: yeahalex BlueSea Uncaptured 'VitalPoint_Beacon3_RC'(team 2). Attackers: Cordierit """ __slots__ = CombatLog.__slots__ matcher = re.compile(r"^Uncaptured\s'(?P[^']+)'\(team\s(?P\d+)\)\.(?:\sAttackers\:\s(?P.*)|)") # Special classes class GameEvent(CombatLog): __slots__ = CombatLog.__slots__ matcher = [ # game session identifier. re.compile(r"^Connect\sto\sgame\ssession\s+(?P\d+)"), # start gameplay identifier. re.compile(r"^Start\sgameplay\s'(?P\w+)'\smap\s+'(?P\w+)',\slocal\sclient\steam\s(?P\d+)"), # pve mission identifier. re.compile(r"^Start\sPVE\smission\s'(?P\w+)'\smap\s+'(?P\w+)'"), ] @classmethod def _log_handler(cls, log): if log.get('log', '').strip().startswith('======='): return True return False def unpack(self, force=False): if self.reviewed and not force: return True self._match_id = None # unpacks the data from the values. # small override to remove trailing "="s in the matching. if hasattr(self, 'matcher') and self.matcher: matchers = self.matcher if not isinstance(matchers, list): matchers = [matchers,] for i, matcher in enumerate(matchers): m = matcher.match(self.values.get('log', '').strip('=').strip()) if m: self.values.update(m.groupdict()) self._match_id = i self.reviewed = True return True # unknown? self.trash = True def clean(self): if 'log' in self.values.keys(): del self.values['log'] class PVE_Mission(CombatLog): """ PVE_Mission: 'bigship_building_normal'. start round 1/3 PVE_Mission: 'bigship_building_normal'. round 1/3. start wave 1/3 PVE_Mission: 'bigship_building_normal'. round 1/3. start wave 2/3 PVE_Mission: 'bigship_building_normal'. round 1/3. start wave 3/3 """ __slots__ = CombatLog.__slots__ matcher = [] # @TODO: do this. class Looted(CombatLog): """ Looted 'ow_Mineral_Info_T3_1' from 'LootCrate_Crystal1' Looted 'Junk_Fuel7' from 'LootCrate_Fuel_Dynamic' Looted 'ow_Afterburner_catalyst' from 'LootCrate_T3_Junk' """ __slots__ = CombatLog.__slots__ matcher = [] # @TODO: do this. class Dropped(CombatLog): """ called on dropping in openspace. """ __slots__ = CombatLog.__slots__ matcher = [] # @TODO: do this. class Set(CombatLog): """ called on setting "relationship" / OpenSpace """ __slots__ = CombatLog.__slots__ matcher = [] #@TODO: do this. class UserEvent(CombatLog): """ special class for combat logs that might be associated with the playing player """ __slots__ = CombatLog.__slots__ @classmethod def _log_handler(cls, log): line = log.get('log', '').strip() if line and 'earned medal' in line: return True elif line: logging.debug('UserEvent saw unknown line: %s' % line) return False # Action? COMBAT_LOGS = [ Apply, Damage, Spawn, Spell, Reward, Participant, Rocket, Heal, Gameplay, #? Scores, Killed, Captured, AddStack, Cancel, Uncaptured, # undone openspace: PVE_Mission, Looted, Set, Dropped, # always last: GameEvent, UserEvent, Stacktrace, ]