#!/usr/bin/env python3 """ # urbanterrorstats # Copyright (c) 2011 Michael Buesch # Licensed under the GNU/GPL version 2 or later. """ import sys from gamestats import * class UTParser(Parser): _re_name = r'(.*)' # Player/item name _re_team = r'(.*)' _re_weapon = r'(.*)' # Weapon name #FIXME _re_esc = r'(?:\^\d)?' re_files_pk3 = re.compile(r'^(\d+) files in pk3 files$') re_gameinit = re.compile(r'^CL_InitCGame:\s+([\d\.]+)\s+seconds$') re_imgdraw = re.compile(r'^(\d+) msec to draw all images$') re_connected = re.compile(r'^' + _re_name + r' connected$') re_disconnected = re.compile(r'^' + _re_name + r' disconnected$') re_enter_game = re.compile(r'^' + _re_name + r' entered the game$') re_join_team = re.compile(r'^' + _re_name + r' joined the ' +\ _re_team + r' team.$') re_join_spec = re.compile(r'^' + _re_name + r' joined the spectators\.$') re_bled_death = re.compile(r'^' + _re_name + r'bled to death from ' +\ _re_name + r"'s attacks\.$") re_you_hit = re.compile(r'^You were hit in the (\w+) by ' + _re_name +\ r' for (\d+)% damage\.$') re_other_hit = re.compile(r'^You hit ' + _re_name +\ r' in the (\w+) for (\d+)% damage\.$') re_fragged0 = re.compile(r'^' + _re_name + r' was on the wrong end of ' +\ _re_name + r"'s " + _re_weapon + r'\.$') re_fragged1 = re.compile(r'^' + _re_name + r' got shredded to pieces by ' +\ _re_name + r"'s " + _re_weapon + r'$') re_fragged2 = re.compile(r'^' + _re_name + r" played 'catch the shiny bullet' with " +\ _re_name + r"'s" + _re_weapon + r' rounds\.$') re_fragged3 = re.compile(r'^' + _re_name + r' has become a nasty stain thanks to ' +\ _re_name + r"'s " + _re_weapon + r'\.$') re_fragged4 = re.compile(r'^' + _re_name + r' danced the ' + _re_weapon +\ r' tango to ' + _re_name + r"'s sweet sweet music\.$") re_fragged5 = re.compile(r'^' + _re_name + r' was torn asunder by ' + _re_name +\ r"'s crass " + _re_weapon + r'\.$') re_fragged6 = re.compile(r'^' + _re_name + r' was ' + _re_weapon + r' spammed without mercy by ' +\ _re_name + r'\.$') re_fragged7 = re.compile(r'^' + _re_name + r' HEARD ' + _re_name + r"'s " +\ _re_weapon + r"\.\.\. didn't AVOID it\. Sucka\.$") re_fragged8 = re.compile(r'^' + _re_name + r' got a whole lot of hole from ' +\ _re_name + r"'s " + _re_weapon + r' round\.$') re_fragged9 = re.compile(r'^' + _re_name + r" was BBQ'ed by " + _re_name +\ r"'s " + _re_weapon + r'\.$') re_fragged10 = re.compile(r'^' + _re_name + r' got nailed to the wall by ' +\ _re_name + r"'s " + _re_weapon + r'$') re_fragged11 = re.compile(r'^' + _re_name + r' was taken out by ' + _re_name +\ r"'s " + _re_weapon + r'\. Plink!$') re_selfkill0 = re.compile(r'^' + _re_name + r' did the lemming thing\.$') re_selfkill1 = re.compile(r'^' + _re_name + r' stepped on his own grenade\.$') re_slowdown = re.compile(r'^' + _re_name + r' managed to slow down ' +\ _re_name + r"'s " + _re_weapon + r' round just a little\.(?: NEEP NEEP!)?$') re_had_health = re.compile(r'^' + _re_name + r' had (\d+)% Health\.$') re_flag_prot = re.compile(r'^' + _re_name + r' protected the ' + _re_name + r' flag\.$') re_flag_ret = re.compile(r'^' + _re_name + r' returned the ' + _re_name + r' flag!$') re_flag_taken = re.compile(r'^' + _re_esc + _re_name + _re_esc + r' has taken the ' +\ _re_esc + _re_name + _re_esc + r' flag!$') re_flag_captured = re.compile(r'^' + _re_esc + _re_name + _re_esc + r' captured the ' +\ _re_esc + _re_name + _re_esc + r' flag!$') re_flag_dropped = re.compile(r'^' + _re_esc + _re_name + _re_esc + r' dropped the ' +\ _re_esc + _re_name + _re_esc + r' flag!$') def __init__(self, options): Parser.__init__(self, options) self.inClientStartup = False def __parseSystemMessages(self, stamp, line): if self.inClientStartup: if line.startswith("Loading vm file"): debugMsg("<<< Game init finished >>>") self.inClientStartup = False return True if line == "----- Client Initialization -----": debugMsg("<<< Initializing game >>>") self.inClientStartup = True return True if line.startswith("---") and line.endswith("---"): debugMsg("Separator (%s)" % line) return True if line.startswith("ioQ3 ") or\ line.startswith("Going through search path") or\ line.startswith("execing ") or\ line.startswith("Hunk_Clear: reset the hunk ok") or\ line.startswith("...loading ") or\ line.startswith("...setting ") or\ line.startswith("...using ") or\ line.startswith("...ignoring ") or\ line.startswith("Initializing OpenGL") or\ line.startswith("GL_RENDERER: ") or\ line.startswith("GL_VENDOR: ") or\ line.startswith("GL_VERSION: ") or\ line.startswith("GL_MAX_TEXTURE_SIZE: ") or\ line.startswith("GL_MAX_ACTIVE_TEXTURES_ARB: ") or\ line.startswith("PIXELFORMAT: ") or\ line.startswith("MODE: ") or\ line.startswith("GAMMA: ") or\ line.startswith("CPU:") or\ line.startswith("Hostname:") or\ line.startswith("IP:") or\ line.startswith("QKEY found.") or\ line.startswith("Initializing Shaders") or\ line.startswith("Initializing SDL") or\ line.startswith("SDL audio driver is") or\ line.startswith("Starting SDL audio") or\ line.startswith("SDL audio initialized") or\ line.startswith("Sound initialization") or\ line.startswith("Sound memory manager") or\ line.startswith("Loading vm file") or\ line.startswith("Opening IP socket: ") or\ line.startswith("Started tty console") or\ line.startswith("RE_Shutdown(") or\ line.startswith("compressed textures: ") or\ line.startswith("Com_TouchMemory: ") or\ line.startswith("Closing SDL audio device") or\ line.startswith("SDL audio device shut down") or\ line.startswith("Shutdown tty console") or\ line.startswith("compilation took ") or\ line.startswith("CL_InitCGame: ") or\ line.find("SDL_Init") >= 0: debugMsg("Misc system message (%s)" % line) return True if line.startswith("console: "): debugMsg("Console (%s)" % line) return True if line == "Don't download your skill. Hackers get Banned!" or\ line == "Welcome until you are banned!": debugMsg("No faggots (%s)" % line) return True m = self.re_files_pk3.match(line) if m: debugMsg("PK3 files (%s)" % line) return True m = self.re_imgdraw.match(line) if m: debugMsg("Image draw (%s)" % line) return True return False def __parseKill(self, timestamp, name0, name1=None, tk=False, sk=False): # name0 fragged name1 if not self.assertCurrentGame("kill"): return player0 = self.currentGame.player(name0) if not sk: player1 = self.currentGame.player(name1) if tk: player0.addTeamkill(timestamp, player1) elif sk: player0.addSuicide(timestamp) else: player0.addFrag(timestamp, player1) if not sk: self.currentGame.hadFirstBlood = True def doParseLine(self, stamp, line): line = line.strip() if not line: return if self.__parseSystemMessages(stamp, line): return m = self.re_connected.match(line) if m: debugMsg("Connected (%s)" % line) return m = self.re_disconnected.match(line) if m: debugMsg("Disconnected (%s)" % line) if not self.assertCurrentGame("player disconnect"): return self.currentGame.player(m.group(1)).disconnected() return m = self.re_enter_game.match(line) if m: debugMsg("Enter game (%s)" % line) if not self.currentGame: self.currentGame = Game(options=self.options, timestamp=stamp, mode="Unknown mode", mapname="Unknown map", selfIDs=("You",)) self.games.append(self.currentGame) self.currentGame.player(m.group(1)).connected() return m = self.re_join_team.match(line) if m: debugMsg("Join team (%s)" % line) return m = self.re_join_spec.match(line) if m: debugMsg("Join spectators (%s)" % line) return m = self.re_bled_death.match(line) if m: debugMsg("Bled to death (%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_you_hit.match(line) if m: debugMsg("You were hit (%s)" % line) return m = self.re_other_hit.match(line) if m: debugMsg("The enemy was hit (%s)" % line) return m = self.re_fragged0.match(line) if m: debugMsg("Fragged 0 (%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_fragged1.match(line) if m: debugMsg("Fragged 1 (%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_fragged2.match(line) if m: debugMsg("Fragged 2 (%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_fragged3.match(line) if m: debugMsg("Fragged 3 (%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_fragged4.match(line) if m: debugMsg("Fragged 4 (%s)" % line) self.__parseKill(stamp, m.group(3), m.group(1)) return m = self.re_fragged5.match(line) if m: debugMsg("Fragged 5 (%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_fragged6.match(line) if m: debugMsg("Fragged 6(%s)" % line) self.__parseKill(stamp, m.group(3), m.group(1)) return m = self.re_fragged7.match(line) if m: debugMsg("Fragged 7(%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_fragged8.match(line) if m: debugMsg("Fragged 8(%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_fragged9.match(line) if m: debugMsg("Fragged 9(%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_fragged10.match(line) if m: debugMsg("Fragged 10(%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_fragged11.match(line) if m: debugMsg("Fragged 11(%s)" % line) self.__parseKill(stamp, m.group(2), m.group(1)) return m = self.re_selfkill0.match(line) if m: debugMsg("Selfkill 0 (%s)" % line) self.__parseKill(stamp, m.group(1), sk=True) return m = self.re_selfkill1.match(line) if m: debugMsg("Selfkill 1 (%s)" % line) self.__parseKill(stamp, m.group(1), sk=True) return m = self.re_slowdown.match(line) if m: debugMsg("Slowdown (%s)" % line) return m = self.re_had_health.match(line) if m: debugMsg("Had health (%s)" % line) return m = self.re_flag_prot.match(line) if m: debugMsg("Protected flag (%s)" % line) return m = self.re_flag_ret.match(line) if m: debugMsg("Returned flag (%s)" % line) return m = self.re_flag_taken.match(line) if m: debugMsg("Took flag (%s)" % line) return m = self.re_flag_captured.match(line) if m: debugMsg("Captured flag (%s)" % line) return m = self.re_flag_dropped.match(line) if m: debugMsg("Dropped flag (%s)" % line) return debugMsg("UNKNOWN console message: '%s'" % line) def main(): return genericMain(scriptname="urbanterrorstats", usageinfo=" Example: Convert and log stats into directory:\n" " ioUrbanTerror.x86_64 | urbanterrorstats -n mynick -l ./logs\n" "\n" " Example: Create stats from logfile:\n" " urbanterrorstats -n mynick ./logs/2011....log", parserClass=UTParser) if __name__ == "__main__": sys.exit(main())