sauerstats 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. #!/usr/bin/env python3
  2. """
  3. # sauerstats.py
  4. # Copyright (c) 2010-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 SauerbratenParser(Parser):
  10. _re_name = r'(.*)' # Player/item name
  11. re_init = re.compile(r'^init: .*$')
  12. re_waypoints = re.compile(r'^loaded (\d+) waypoints from ' + _re_name + r'$')
  13. re_intermission = re.compile(r'^intermission:$')
  14. re_info = re.compile(r'^Info: .*$')
  15. re_rendering = re.compile(r'^Rendering using .*$')
  16. re_game_mode = re.compile(r'^game mode is ' + _re_name + r'$')
  17. re_game_ended = re.compile(r'^game has ended!$')
  18. re_you_disconn = re.compile(r'^disconnected$')
  19. re_player_disconn = re.compile(r'^player ' + _re_name + r' disconnected$')
  20. re_conn_attempt = re.compile(r'^attempting to connect.*$')
  21. re_connected = re.compile(r'^connected to server$')
  22. re_player_connected = re.compile(r'^connected: ' + _re_name + r'$')
  23. re_connected_from = re.compile(r'^' + _re_name + r' connected from ' +\
  24. _re_name + r'$')
  25. re_connected_from2 = re.compile(r'^' + _re_name + r' is fragging in ' +\
  26. _re_name + r'$')
  27. re_disconn_attempt = re.compile(r'^attempting to disconnect\.\.\.$')
  28. re_rename = re.compile(r'^' + _re_name + r' is now known as ' + _re_name + r'$')
  29. re_read_map = re.compile(r'^read map (.*) \(\d+\.\d+ seconds\)$')
  30. re_suggest = re.compile(r'^' + _re_name + r' suggests ' + _re_name + r' on map ' +
  31. _re_name + r' \(select map to vote\)$')
  32. re_you_got_fragged = re.compile(r'^you got fragged by ' + _re_name + r'$')
  33. re_fragged = re.compile(r'^' + _re_name + r' fragged ' + _re_name + r'$')
  34. re_fragged_teammate = re.compile(r'^' + _re_name + r' fragged a teammate \(' +\
  35. _re_name + r'\)$')
  36. re_yougotkilledby_teammate = re.compile(r'^you got fragged by a teammate \(' +\
  37. _re_name + r'\)$')
  38. re_suicide = re.compile(r'^' + _re_name + r' suicided!?$')
  39. re_dropped_your = re.compile(r'^' + _re_name + r' dropped your flag$')
  40. re_dropped_enemy = re.compile(r'^' + _re_name + r' dropped the enemy flag$')
  41. re_stole_your = re.compile(r'^' + _re_name + r' stole your flag$')
  42. re_stole_enemy = re.compile(r'^' + _re_name + r' stole the enemy flag$')
  43. re_picked_your = re.compile(r'^' + _re_name + r' picked up your flag$')
  44. re_picked_enemy = re.compile(r'^' + _re_name + r' picked up the enemy flag$')
  45. re_score_your = re.compile(r'^' + _re_name + r' scored for your team$')
  46. re_score_enemy = re.compile(r'^' + _re_name + r' scored for the enemy team$')
  47. re_returned_your = re.compile(r'^' + _re_name + r' returned your flag$')
  48. re_returned_enemy = re.compile(r'^' + _re_name + r' returned the enemy flag$')
  49. re_stopped_by = re.compile(r'^' + _re_name + r' was stopped by ' + _re_name + r'$')
  50. re_rampage = re.compile(r'^' + _re_name + r' is on a RAMPAGE!!$')
  51. re_dominate = re.compile(r'^' + _re_name + r' is DOMINATING!!$')
  52. re_triplekill = re.compile(r'^' + _re_name + r' scored a TRIPLE KILL!$')
  53. re_msg = re.compile(r'^' + _re_name + r': .*$')
  54. # chat annotation patch
  55. re_chat = re.compile(r'^chat-message: ' + _re_name + r':\s*(.*)$')
  56. re_teamchat = re.compile(r'^teamchat-message: ' + _re_name + r':\s*(.*)$')
  57. def __init__(self, options):
  58. Parser.__init__(self, options)
  59. self.lastReadMap = "unknown"
  60. def __parseKill(self, timestamp, name0, name1, tk=False):
  61. # name0 fragged name1
  62. if not self.assertCurrentGame("kill"):
  63. return
  64. player0 = self.currentGame.playerOrMe(name0)
  65. player1 = self.currentGame.playerOrMe(name1)
  66. if tk:
  67. player0.addTeamkill(timestamp, player1)
  68. else:
  69. player0.addFrag(timestamp, player1)
  70. self.currentGame.hadFirstBlood = True
  71. def doParseLine(self, stamp, line):
  72. line = line.strip()
  73. m = self.re_chat.match(line)
  74. if m:
  75. debugMsg("Chat (%s: %s)" % (m.group(1), m.group(2)))
  76. return
  77. m = self.re_teamchat.match(line)
  78. if m:
  79. debugMsg("Teamchat (%s: %s)" % (m.group(1), m.group(2)))
  80. return
  81. m = self.re_info.match(line)
  82. if m:
  83. debugMsg("Info (%s)" % line)
  84. return
  85. m = self.re_game_ended.match(line)
  86. if m:
  87. if not self.assertCurrentGame("game ended"):
  88. return
  89. debugMsg("Game ended (%s)" % line)
  90. if self.currentGame.ended:
  91. print("Current game ended twice?!")
  92. return
  93. self.currentGame.ended = stamp
  94. self.gameEnded()
  95. return
  96. m = self.re_game_mode.match(line)
  97. if m:
  98. self.currentGame = None
  99. debugMsg("Game mode (%s)" % line)
  100. newGame = Game(options=self.options,
  101. timestamp=stamp, mode=m.group(1),
  102. mapname=self.lastReadMap,
  103. selfIDs=("you",))
  104. self.currentGame = newGame
  105. self.games.append(newGame)
  106. return
  107. m = self.re_yougotkilledby_teammate.match(line)
  108. if m:
  109. debugMsg("TK 1 (%s)" % line)
  110. self.__parseKill(stamp, m.group(1), None, tk=True)
  111. return
  112. m = self.re_fragged_teammate.match(line)
  113. if m:
  114. debugMsg("TK 2 (%s)" % line)
  115. self.__parseKill(stamp, m.group(1), m.group(2), tk=True)
  116. return
  117. m = self.re_you_got_fragged.match(line)
  118. if m:
  119. debugMsg("Frag 1 (%s)" % line)
  120. self.__parseKill(stamp, m.group(1), None)
  121. return
  122. m = self.re_fragged.match(line)
  123. if m:
  124. debugMsg("Frag 2 (%s)" % line)
  125. self.__parseKill(stamp, m.group(1), m.group(2))
  126. return
  127. m = self.re_suicide.match(line)
  128. if m:
  129. debugMsg("Suicide (%s)" % line)
  130. if not self.assertCurrentGame("suicide"):
  131. return
  132. player = self.currentGame.playerOrMe(m.group(1))
  133. player.addSuicide(stamp)
  134. return
  135. m = self.re_dropped_your.match(line)
  136. if m:
  137. debugMsg("Dropped your flag (%s)" % line)
  138. if not self.assertCurrentGame("dropped-your"):
  139. return
  140. player = self.currentGame.player(m.group(1))
  141. player.addCtfDrop(stamp)
  142. return
  143. m = self.re_dropped_enemy.match(line)
  144. if m:
  145. debugMsg("Dropped enemy flag (%s)" % line)
  146. if not self.assertCurrentGame("dropped-enemy"):
  147. return
  148. player = self.currentGame.playerOrMe(m.group(1))
  149. player.addCtfDrop(stamp)
  150. return
  151. m = self.re_stole_your.match(line)
  152. if m:
  153. debugMsg("Stole your flag (%s)" % line)
  154. if not self.assertCurrentGame("stole-your"):
  155. return
  156. player = self.currentGame.player(m.group(1))
  157. player.addCtfSteal(stamp)
  158. return
  159. m = self.re_stole_enemy.match(line)
  160. if m:
  161. debugMsg("Stole enemy flag (%s)" % line)
  162. if not self.assertCurrentGame("stole-enemy"):
  163. return
  164. player = self.currentGame.playerOrMe(m.group(1))
  165. player.addCtfSteal(stamp)
  166. return
  167. m = self.re_picked_your.match(line)
  168. if m:
  169. debugMsg("Picked your flag (%s)" % line)
  170. if not self.assertCurrentGame("picked-your"):
  171. return
  172. player = self.currentGame.player(m.group(1))
  173. player.addCtfPick(stamp)
  174. return
  175. m = self.re_picked_enemy.match(line)
  176. if m:
  177. debugMsg("Picked enemy flag (%s)" % line)
  178. if not self.assertCurrentGame("picked-enemy"):
  179. return
  180. player = self.currentGame.playerOrMe(m.group(1))
  181. player.addCtfPick(stamp)
  182. return
  183. m = self.re_score_your.match(line)
  184. if m:
  185. debugMsg("Scored for your team (%s)" % line)
  186. if not self.assertCurrentGame("scored-your"):
  187. return
  188. player = self.currentGame.playerOrMe(m.group(1))
  189. player.addCtfScore(stamp)
  190. return
  191. m = self.re_score_enemy.match(line)
  192. if m:
  193. debugMsg("Scored for enemy team (%s)" % line)
  194. if not self.assertCurrentGame("scored-enemy"):
  195. return
  196. player = self.currentGame.player(m.group(1))
  197. player.addCtfScore(stamp)
  198. return
  199. m = self.re_returned_your.match(line)
  200. if m:
  201. debugMsg("Returned your flag (%s)" % line)
  202. if not self.assertCurrentGame("returned-your"):
  203. return
  204. player = self.currentGame.playerOrMe(m.group(1))
  205. player.addCtfReturn(stamp)
  206. return
  207. m = self.re_returned_enemy.match(line)
  208. if m:
  209. debugMsg("Returned enemy flag (%s)" % line)
  210. if not self.assertCurrentGame("returned-enemy"):
  211. return
  212. player = self.currentGame.player(m.group(1))
  213. player.addCtfReturn(stamp)
  214. return
  215. m = self.re_stopped_by.match(line)
  216. if m:
  217. debugMsg("Stopped by (%s)" % line)
  218. return
  219. m = self.re_rampage.match(line)
  220. if m:
  221. debugMsg("Rampage (%s)" % line)
  222. return
  223. m = self.re_dominate.match(line)
  224. if m:
  225. debugMsg("Dominate (%s)" % line)
  226. return
  227. m = self.re_triplekill.match(line)
  228. if m:
  229. debugMsg("Triplekill (%s)" % line)
  230. return
  231. m = self.re_suggest.match(line)
  232. if m:
  233. debugMsg("Suggest (%s)" % line)
  234. return
  235. m = self.re_player_connected.match(line)
  236. if m:
  237. debugMsg("Player connected (%s)" % line)
  238. if not self.assertCurrentGame("player connected"):
  239. return
  240. name = m.group(1)
  241. self.currentGame.player(name).connected()
  242. return
  243. m = self.re_player_disconn.match(line)
  244. if m:
  245. debugMsg("Player disconnected (%s)" % line)
  246. if not self.assertCurrentGame("player connected"):
  247. return
  248. name = m.group(1)
  249. self.currentGame.player(name).disconnected()
  250. return
  251. m = self.re_conn_attempt.match(line)
  252. if m:
  253. debugMsg("Connection attempt (%s)" % line)
  254. return
  255. m = self.re_connected.match(line)
  256. if m:
  257. debugMsg("Connected (%s)" % line)
  258. return
  259. m = self.re_connected_from.match(line)
  260. if m:
  261. debugMsg("Connected from (%s)" % line)
  262. return
  263. m = self.re_connected_from2.match(line)
  264. if m:
  265. debugMsg("Connected from (%s)" % line)
  266. return
  267. m = self.re_disconn_attempt.match(line)
  268. if m:
  269. debugMsg("Disconnect attempt (%s)" % line)
  270. return
  271. m = self.re_you_disconn.match(line)
  272. if m:
  273. debugMsg("Disconnected (%s)" % line)
  274. return
  275. m = self.re_rename.match(line)
  276. if m:
  277. debugMsg("Player rename (%s)" % line)
  278. if not self.assertCurrentGame("player rename"):
  279. return
  280. oldName = m.group(1)
  281. newName = m.group(2)
  282. self.currentGame.player(oldName).rename(newName)
  283. return
  284. m = self.re_read_map.match(line)
  285. if m:
  286. if self.options.splitLogs:
  287. closeRawLog()
  288. debugMsg("Map (%s)" % line)
  289. mapname = m.group(1)
  290. mapname = mapname.split('/')[-1]
  291. if mapname.endswith(".ogz"):
  292. mapname = mapname[0:-4]
  293. self.lastReadMap = mapname
  294. return
  295. m = self.re_rendering.match(line)
  296. if m:
  297. debugMsg("Renderer (%s)" % line)
  298. return
  299. m = self.re_init.match(line)
  300. if m:
  301. debugMsg("Init (%s)" % line)
  302. return
  303. m = self.re_waypoints.match(line)
  304. if m:
  305. debugMsg("Waypoints (%s)" % line)
  306. return
  307. m = self.re_intermission.match(line)
  308. if m:
  309. debugMsg("Intermission (%s)" % line)
  310. return
  311. m = self.re_msg.match(line)
  312. if m:
  313. debugMsg("Message (%s)" % line)
  314. return
  315. if not line.replace("*", "") or\
  316. not line.replace(">", ""):
  317. debugMsg("Spacer (%s)" % line)
  318. return
  319. debugMsg("UNKNOWN console message: '%s'" % line)
  320. def main():
  321. return genericMain(scriptname="sauerstats",
  322. usageinfo=" Example: Convert and log stats into directory:\n"
  323. " sauerbraten_unix | sauerstats -n mynick -l ./logs\n"
  324. "\n"
  325. " Example: Create stats from logfile:\n"
  326. " sauerstats -n mynick ./logs/2011....log",
  327. parserClass=SauerbratenParser)
  328. if __name__ == "__main__":
  329. sys.exit(main())