urbanterrorstats 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #!/usr/bin/env python3
  2. """
  3. # urbanterrorstats
  4. # Copyright (c) 2011 Michael Buesch <m@bues.ch>
  5. # Licensed under the GNU/GPL version 2 or later.
  6. """
  7. import sys
  8. from gamestats import *
  9. class UTParser(Parser):
  10. _re_name = r'(.*)' # Player/item name
  11. _re_team = r'(.*)'
  12. _re_weapon = r'(.*)' # Weapon name #FIXME
  13. _re_esc = r'(?:\^\d)?'
  14. re_files_pk3 = re.compile(r'^(\d+) files in pk3 files$')
  15. re_gameinit = re.compile(r'^CL_InitCGame:\s+([\d\.]+)\s+seconds$')
  16. re_imgdraw = re.compile(r'^(\d+) msec to draw all images$')
  17. re_connected = re.compile(r'^' + _re_name + r' connected$')
  18. re_disconnected = re.compile(r'^' + _re_name + r' disconnected$')
  19. re_enter_game = re.compile(r'^' + _re_name + r' entered the game$')
  20. re_join_team = re.compile(r'^' + _re_name + r' joined the ' +\
  21. _re_team + r' team.$')
  22. re_join_spec = re.compile(r'^' + _re_name + r' joined the spectators\.$')
  23. re_bled_death = re.compile(r'^' + _re_name + r'bled to death from ' +\
  24. _re_name + r"'s attacks\.$")
  25. re_you_hit = re.compile(r'^You were hit in the (\w+) by ' + _re_name +\
  26. r' for (\d+)% damage\.$')
  27. re_other_hit = re.compile(r'^You hit ' + _re_name +\
  28. r' in the (\w+) for (\d+)% damage\.$')
  29. re_fragged0 = re.compile(r'^' + _re_name + r' was on the wrong end of ' +\
  30. _re_name + r"'s " + _re_weapon + r'\.$')
  31. re_fragged1 = re.compile(r'^' + _re_name + r' got shredded to pieces by ' +\
  32. _re_name + r"'s " + _re_weapon + r'$')
  33. re_fragged2 = re.compile(r'^' + _re_name + r" played 'catch the shiny bullet' with " +\
  34. _re_name + r"'s" + _re_weapon + r' rounds\.$')
  35. re_fragged3 = re.compile(r'^' + _re_name + r' has become a nasty stain thanks to ' +\
  36. _re_name + r"'s " + _re_weapon + r'\.$')
  37. re_fragged4 = re.compile(r'^' + _re_name + r' danced the ' + _re_weapon +\
  38. r' tango to ' + _re_name + r"'s sweet sweet music\.$")
  39. re_fragged5 = re.compile(r'^' + _re_name + r' was torn asunder by ' + _re_name +\
  40. r"'s crass " + _re_weapon + r'\.$')
  41. re_fragged6 = re.compile(r'^' + _re_name + r' was ' + _re_weapon + r' spammed without mercy by ' +\
  42. _re_name + r'\.$')
  43. re_fragged7 = re.compile(r'^' + _re_name + r' HEARD ' + _re_name + r"'s " +\
  44. _re_weapon + r"\.\.\. didn't AVOID it\. Sucka\.$")
  45. re_fragged8 = re.compile(r'^' + _re_name + r' got a whole lot of hole from ' +\
  46. _re_name + r"'s " + _re_weapon + r' round\.$')
  47. re_fragged9 = re.compile(r'^' + _re_name + r" was BBQ'ed by " + _re_name +\
  48. r"'s " + _re_weapon + r'\.$')
  49. re_fragged10 = re.compile(r'^' + _re_name + r' got nailed to the wall by ' +\
  50. _re_name + r"'s " + _re_weapon + r'$')
  51. re_fragged11 = re.compile(r'^' + _re_name + r' was taken out by ' + _re_name +\
  52. r"'s " + _re_weapon + r'\. Plink!$')
  53. re_selfkill0 = re.compile(r'^' + _re_name + r' did the lemming thing\.$')
  54. re_selfkill1 = re.compile(r'^' + _re_name + r' stepped on his own grenade\.$')
  55. re_slowdown = re.compile(r'^' + _re_name + r' managed to slow down ' +\
  56. _re_name + r"'s " + _re_weapon + r' round just a little\.(?: NEEP NEEP!)?$')
  57. re_had_health = re.compile(r'^' + _re_name + r' had (\d+)% Health\.$')
  58. re_flag_prot = re.compile(r'^' + _re_name + r' protected the ' + _re_name + r' flag\.$')
  59. re_flag_ret = re.compile(r'^' + _re_name + r' returned the ' + _re_name + r' flag!$')
  60. re_flag_taken = re.compile(r'^' + _re_esc + _re_name + _re_esc + r' has taken the ' +\
  61. _re_esc + _re_name + _re_esc + r' flag!$')
  62. re_flag_captured = re.compile(r'^' + _re_esc + _re_name + _re_esc + r' captured the ' +\
  63. _re_esc + _re_name + _re_esc + r' flag!$')
  64. re_flag_dropped = re.compile(r'^' + _re_esc + _re_name + _re_esc + r' dropped the ' +\
  65. _re_esc + _re_name + _re_esc + r' flag!$')
  66. def __init__(self, options):
  67. Parser.__init__(self, options)
  68. self.inClientStartup = False
  69. def __parseSystemMessages(self, stamp, line):
  70. if self.inClientStartup:
  71. if line.startswith("Loading vm file"):
  72. debugMsg("<<< Game init finished >>>")
  73. self.inClientStartup = False
  74. return True
  75. if line == "----- Client Initialization -----":
  76. debugMsg("<<< Initializing game >>>")
  77. self.inClientStartup = True
  78. return True
  79. if line.startswith("---") and line.endswith("---"):
  80. debugMsg("Separator (%s)" % line)
  81. return True
  82. if line.startswith("ioQ3 ") or\
  83. line.startswith("Going through search path") or\
  84. line.startswith("execing ") or\
  85. line.startswith("Hunk_Clear: reset the hunk ok") or\
  86. line.startswith("...loading ") or\
  87. line.startswith("...setting ") or\
  88. line.startswith("...using ") or\
  89. line.startswith("...ignoring ") or\
  90. line.startswith("Initializing OpenGL") or\
  91. line.startswith("GL_RENDERER: ") or\
  92. line.startswith("GL_VENDOR: ") or\
  93. line.startswith("GL_VERSION: ") or\
  94. line.startswith("GL_MAX_TEXTURE_SIZE: ") or\
  95. line.startswith("GL_MAX_ACTIVE_TEXTURES_ARB: ") or\
  96. line.startswith("PIXELFORMAT: ") or\
  97. line.startswith("MODE: ") or\
  98. line.startswith("GAMMA: ") or\
  99. line.startswith("CPU:") or\
  100. line.startswith("Hostname:") or\
  101. line.startswith("IP:") or\
  102. line.startswith("QKEY found.") or\
  103. line.startswith("Initializing Shaders") or\
  104. line.startswith("Initializing SDL") or\
  105. line.startswith("SDL audio driver is") or\
  106. line.startswith("Starting SDL audio") or\
  107. line.startswith("SDL audio initialized") or\
  108. line.startswith("Sound initialization") or\
  109. line.startswith("Sound memory manager") or\
  110. line.startswith("Loading vm file") or\
  111. line.startswith("Opening IP socket: ") or\
  112. line.startswith("Started tty console") or\
  113. line.startswith("RE_Shutdown(") or\
  114. line.startswith("compressed textures: ") or\
  115. line.startswith("Com_TouchMemory: ") or\
  116. line.startswith("Closing SDL audio device") or\
  117. line.startswith("SDL audio device shut down") or\
  118. line.startswith("Shutdown tty console") or\
  119. line.startswith("compilation took ") or\
  120. line.startswith("CL_InitCGame: ") or\
  121. line.find("SDL_Init") >= 0:
  122. debugMsg("Misc system message (%s)" % line)
  123. return True
  124. if line.startswith("console: "):
  125. debugMsg("Console (%s)" % line)
  126. return True
  127. if line == "Don't download your skill. Hackers get Banned!" or\
  128. line == "Welcome until you are banned!":
  129. debugMsg("No faggots (%s)" % line)
  130. return True
  131. m = self.re_files_pk3.match(line)
  132. if m:
  133. debugMsg("PK3 files (%s)" % line)
  134. return True
  135. m = self.re_imgdraw.match(line)
  136. if m:
  137. debugMsg("Image draw (%s)" % line)
  138. return True
  139. return False
  140. def __parseKill(self, timestamp, name0, name1=None, tk=False, sk=False):
  141. # name0 fragged name1
  142. if not self.assertCurrentGame("kill"):
  143. return
  144. player0 = self.currentGame.player(name0)
  145. if not sk:
  146. player1 = self.currentGame.player(name1)
  147. if tk:
  148. player0.addTeamkill(timestamp, player1)
  149. elif sk:
  150. player0.addSuicide(timestamp)
  151. else:
  152. player0.addFrag(timestamp, player1)
  153. if not sk:
  154. self.currentGame.hadFirstBlood = True
  155. def doParseLine(self, stamp, line):
  156. line = line.strip()
  157. if not line:
  158. return
  159. if self.__parseSystemMessages(stamp, line):
  160. return
  161. m = self.re_connected.match(line)
  162. if m:
  163. debugMsg("Connected (%s)" % line)
  164. return
  165. m = self.re_disconnected.match(line)
  166. if m:
  167. debugMsg("Disconnected (%s)" % line)
  168. if not self.assertCurrentGame("player disconnect"):
  169. return
  170. self.currentGame.player(m.group(1)).disconnected()
  171. return
  172. m = self.re_enter_game.match(line)
  173. if m:
  174. debugMsg("Enter game (%s)" % line)
  175. if not self.currentGame:
  176. self.currentGame = Game(options=self.options,
  177. timestamp=stamp,
  178. mode="Unknown mode",
  179. mapname="Unknown map",
  180. selfIDs=("You",))
  181. self.games.append(self.currentGame)
  182. self.currentGame.player(m.group(1)).connected()
  183. return
  184. m = self.re_join_team.match(line)
  185. if m:
  186. debugMsg("Join team (%s)" % line)
  187. return
  188. m = self.re_join_spec.match(line)
  189. if m:
  190. debugMsg("Join spectators (%s)" % line)
  191. return
  192. m = self.re_bled_death.match(line)
  193. if m:
  194. debugMsg("Bled to death (%s)" % line)
  195. self.__parseKill(stamp, m.group(2), m.group(1))
  196. return
  197. m = self.re_you_hit.match(line)
  198. if m:
  199. debugMsg("You were hit (%s)" % line)
  200. return
  201. m = self.re_other_hit.match(line)
  202. if m:
  203. debugMsg("The enemy was hit (%s)" % line)
  204. return
  205. m = self.re_fragged0.match(line)
  206. if m:
  207. debugMsg("Fragged 0 (%s)" % line)
  208. self.__parseKill(stamp, m.group(2), m.group(1))
  209. return
  210. m = self.re_fragged1.match(line)
  211. if m:
  212. debugMsg("Fragged 1 (%s)" % line)
  213. self.__parseKill(stamp, m.group(2), m.group(1))
  214. return
  215. m = self.re_fragged2.match(line)
  216. if m:
  217. debugMsg("Fragged 2 (%s)" % line)
  218. self.__parseKill(stamp, m.group(2), m.group(1))
  219. return
  220. m = self.re_fragged3.match(line)
  221. if m:
  222. debugMsg("Fragged 3 (%s)" % line)
  223. self.__parseKill(stamp, m.group(2), m.group(1))
  224. return
  225. m = self.re_fragged4.match(line)
  226. if m:
  227. debugMsg("Fragged 4 (%s)" % line)
  228. self.__parseKill(stamp, m.group(3), m.group(1))
  229. return
  230. m = self.re_fragged5.match(line)
  231. if m:
  232. debugMsg("Fragged 5 (%s)" % line)
  233. self.__parseKill(stamp, m.group(2), m.group(1))
  234. return
  235. m = self.re_fragged6.match(line)
  236. if m:
  237. debugMsg("Fragged 6(%s)" % line)
  238. self.__parseKill(stamp, m.group(3), m.group(1))
  239. return
  240. m = self.re_fragged7.match(line)
  241. if m:
  242. debugMsg("Fragged 7(%s)" % line)
  243. self.__parseKill(stamp, m.group(2), m.group(1))
  244. return
  245. m = self.re_fragged8.match(line)
  246. if m:
  247. debugMsg("Fragged 8(%s)" % line)
  248. self.__parseKill(stamp, m.group(2), m.group(1))
  249. return
  250. m = self.re_fragged9.match(line)
  251. if m:
  252. debugMsg("Fragged 9(%s)" % line)
  253. self.__parseKill(stamp, m.group(2), m.group(1))
  254. return
  255. m = self.re_fragged10.match(line)
  256. if m:
  257. debugMsg("Fragged 10(%s)" % line)
  258. self.__parseKill(stamp, m.group(2), m.group(1))
  259. return
  260. m = self.re_fragged11.match(line)
  261. if m:
  262. debugMsg("Fragged 11(%s)" % line)
  263. self.__parseKill(stamp, m.group(2), m.group(1))
  264. return
  265. m = self.re_selfkill0.match(line)
  266. if m:
  267. debugMsg("Selfkill 0 (%s)" % line)
  268. self.__parseKill(stamp, m.group(1), sk=True)
  269. return
  270. m = self.re_selfkill1.match(line)
  271. if m:
  272. debugMsg("Selfkill 1 (%s)" % line)
  273. self.__parseKill(stamp, m.group(1), sk=True)
  274. return
  275. m = self.re_slowdown.match(line)
  276. if m:
  277. debugMsg("Slowdown (%s)" % line)
  278. return
  279. m = self.re_had_health.match(line)
  280. if m:
  281. debugMsg("Had health (%s)" % line)
  282. return
  283. m = self.re_flag_prot.match(line)
  284. if m:
  285. debugMsg("Protected flag (%s)" % line)
  286. return
  287. m = self.re_flag_ret.match(line)
  288. if m:
  289. debugMsg("Returned flag (%s)" % line)
  290. return
  291. m = self.re_flag_taken.match(line)
  292. if m:
  293. debugMsg("Took flag (%s)" % line)
  294. return
  295. m = self.re_flag_captured.match(line)
  296. if m:
  297. debugMsg("Captured flag (%s)" % line)
  298. return
  299. m = self.re_flag_dropped.match(line)
  300. if m:
  301. debugMsg("Dropped flag (%s)" % line)
  302. return
  303. debugMsg("UNKNOWN console message: '%s'" % line)
  304. def main():
  305. return genericMain(scriptname="urbanterrorstats",
  306. usageinfo=" Example: Convert and log stats into directory:\n"
  307. " ioUrbanTerror.x86_64 | urbanterrorstats -n mynick -l ./logs\n"
  308. "\n"
  309. " Example: Create stats from logfile:\n"
  310. " urbanterrorstats -n mynick ./logs/2011....log",
  311. parserClass=UTParser)
  312. if __name__ == "__main__":
  313. sys.exit(main())