#!/usr/bin/python # -*- coding: utf-8 -*- from logs.base import Log, L_WARNING, Stacktrace import re """ This deals with the Game.Log file This file records lots of junk, but is needed to establish actions taken between combat sessions, or retrieve more detailed information about running instances. It is also the typical place for a Stacktrace to happen. -------------------------------------- Interesting Lines: 23:16:27.427 | Steam initialized appId 212070, userSteamID 1|1|4c5a01, userName 'G4bOrg' 23:16:36.214 | ====== starting level: 'levels/mainmenu/mainmenu' ====== 23:16:38.822 | ====== level started: 'levels/mainmenu/mainmenu' success ====== 23:16:44.251 | ====== starting level: 'levels\mainmenu\mm_empire' ====== 23:16:46.464 | ====== level started: 'levels\mainmenu\mm_empire' success ====== --- Date: 2014-07-18 (Fri Jul 2014) Mitteleuropäische Sommerzeit UTC+01:00 23:55:55.517 | MasterServerSession: connect to dedicated server, session 6777304, at addr 159.253.138.162|35005 23:55:55.543 | client: start connecting to 159.253.138.162|35005... 23:55:55.683 | client: connected to 159.253.138.162|35005, setting up session... 23:55:55.886 | client: ADD_PLAYER 0 (OregyenDuero [OWL], 00039C86) status 6 team 1 group 1178422 23:55:55.886 | client: ADD_PLAYER 1 (R0gue, 0012768A) status 6 team 2 group 1178451 23:55:55.886 | client: ADD_PLAYER 2 (g4borg [OWL], 0003A848) status 1 team 1 group 1178422 23:55:55.886 | client: ADD_PLAYER 3 (WladTepes, 001210D8) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 4 (oberus [], 000FE9B2) status 6 team 2 23:55:55.886 | client: ADD_PLAYER 5 (TheGuns58, 00121C58) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 6 (Belleraphon, 0004C744) status 2 team 2 23:55:55.886 | client: ADD_PLAYER 7 (TopoL, 00007E1F) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 8 (unicoimbraPT, 000C4FAC) status 6 team 2 23:55:55.886 | client: ADD_PLAYER 9 (AeroBobik [], 00082047) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 10 (Samson4321 [], 000B93AF) status 6 team 2 23:55:55.886 | client: ADD_PLAYER 11 (nol [], 00069165) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 12 (Pudwoppa, 000334A4) status 2 team 2 23:55:55.886 | client: ADD_PLAYER 13 (IgorMad [], 000D2AF3) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 14 (YokaI, 000F1CC9) status 6 team 2 23:55:55.886 | client: ADD_PLAYER 15 (MrAnyKey [], 0012246C) status 6 team 2 group 1178451 23:55:55.886 | client: ADD_PLAYER 30 ((bot)David, 00000000) status 4 team 1 23:55:55.886 | client: ADD_PLAYER 31 ((bot)George, 00000000) status 4 team 2 23:55:55.886 | client: server assigned id 2 23:55:55.886 | client: got level load message 's1340_thar_aliendebris13' 23:55:55.889 | reset d3d device 23:55:56.487 | ReplayManager: stopping activity due to map change 23:55:56.576 | ====== starting level: 'levels\area2\s1340_thar_aliendebris13' KingOfTheHill client ====== """ class GameLog(Log): __slots__ = ['matcher', 'trash', '_match_id', 'values'] @classmethod def is_handler(cls, log): if log.get('logtype', None) == '': # we handle only logs with empty logtype. return cls._is_handler(log) return False @classmethod def _is_handler(cls, log): return False def __init__(self, values=None): self.values = values self.reviewed = False def clean(self): if 'log' in self.values.keys(): del self.values['log'] 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? self.trash = True def explain(self): ''' returns a String readable by humans explaining this Log ''' return self.values.get('log', 'Unknown Game Log') class WarningLog(Log): __slots__ = ['trash',] trash = True @classmethod def is_handler(cls, log): if log.get('logtype', None) == L_WARNING: return True return False def __init__(self, values=None): pass ######################################################################################################## # Individual logs. class SteamInitialization(GameLog): matcher = [ re.compile(r"^Steam\sinitialized\sappId\s(?P\d+),\suserSteamID\s(?P\d+)\|(?P\d+)\|(?P\w+),\suserName\s'(?P[^']+)'"), ] class MasterServerSession(GameLog): matcher = [ re.compile(r"^MasterServerSession\:\sconnect\sto\sdedicated\sserver(?:,\s|session\s(?P\d+)|at addr (?P\d+\.\d+\.\d+\.\d+)\|(?P\d+))+"), re.compile(r"^MasterServerSession:\sconnect\sto\sZoneInstance,\ssession\s(?P\d+),\sat\saddr\s(?P\d+\.\d+\.\d+\.\d+)\|(?P\d+),\szoneId\s(?P\d+)"), ] @classmethod def _is_handler(cls, log): if log.get('log', '').startswith('MasterServerSession'): return True return False class ClientInfo(GameLog): # Note: clinfo holds the subtype of this packet. matcher = [ # connecting; addr, port re.compile(r"^client\:\sstart\s(?Pconnecting)\sto\s(?P\d+\.\d+\.\d+\.\d+)\|(?P\d+)\.\.\."), # connected; addr, port re.compile(r"^client\:\s(?Pconnected)\sto\s(?P\d+\.\d+\.\d+\.\d+)\|(?P\d+).*"), # ADD_PLAYER; pnr, player, clantag, player_id, status, team, group re.compile(r"^client\:\s(?PADD_PLAYER)\s+(?P\d+)\s+\((?P[^\s\,]+)(?:\s\[(?P\w+)\],|\s\[\],|,)\s(?P\w+)\)(?:\s|status\s(?P\d+)|team\s(?P\d+)|group\s(?P\d+))+"), # assigned; myid re.compile(r"^client\:\sserver\s(?Passigned)\sid\s(?P\d+)"), # level; level re.compile(r"^client\:\sgot\s(?Plevel)\sload\smessage\s'(?P[^']+)'"), # leave; pnr re.compile(r"^client\:\splayer\s(?P\d+)\s(?Pleave)\sgame"), # avgPing; avg_ping, avg_packet_loss, avg_snapshot_loss re.compile(r"^client\:\s(?PavgPing)\s(?P[^;]+)(?:\;|\s|avgPacketLoss\s(?P[^;]+)|avgSnapshotLoss\s(?P[^;$]+))+"), # closed; dr re.compile(r"^client\:\sconnection\s(?Pclosed)\.(?:\s|(?P.*))+"), # disconnect; addr, port, dr re.compile(r"^client\:\s(?Pdisconnect)\sfrom\sserver\s(?P\d+\.\d+\.\d+\.\d+)\|(?P\d+)\.(?:\s|(?P\w+))+"), # ready; re.compile(r"^client\:\ssend\s(?Pready)\smessage"), # init; ping re.compile(r"^client\:\sgot\s(?Pinit)\smessage\s+\(and\s1st\ssnapshot\)\.\sping\s(?P\d+)"), ] @classmethod def _is_handler(cls, log): if log.get('log', '').startswith('client:'): return True return False class StartingLevel(GameLog): # level, gametype, unknown_gametype matcher = [ re.compile(r"^======\sstarting\slevel\:\s'(?P[^']+)'(?:\s|client|(?PKingOfTheHill)|(?P[^\s]+))+======"), ] @classmethod def _is_handler(cls, log): if log.get('log', '').startswith('====== starting'): return True return False class LevelStarted(GameLog): matcher = [] @classmethod def _is_handler(cls, log): if log.get('log', '').startswith('====== level'): return True return False GAME_LOGS = [#SteamInitialization, MasterServerSession, ClientInfo, StartingLevel, #LevelStarted, Stacktrace, ]