mainly more work on examples

but also first logic in somewhat usable battle_factory
initial timestamping method (TBD)
This commit is contained in:
Gabor Körber 2017-05-22 20:12:15 +02:00
parent a5d353e302
commit 39bc7604bf
6 changed files with 123 additions and 46 deletions

View File

@ -38,8 +38,8 @@ if __name__ == '__main__':
COUNT_GOOD = True # count via rex good packets aswell. useful to see total encountered packets in summary.
LOG_GOOD_ONLY = False # Log good packets only. if set to false, will log unknown packets to trash_log.
LOG_BAD_CMBT = True # by default, the main logs of interest for unknown entries is combat logs. here you can finetune which logs to catch.
LOG_BAD_CHAT = False
LOG_BAD_GAME = False
LOG_BAD_CHAT = True
LOG_BAD_GAME = True
# set up our logging to do our task:
FILE_MAIN_LOG = 'scon.log.bak'
@ -123,7 +123,10 @@ if __name__ == '__main__':
if not l.unpack() or COUNT_GOOD:
rex_game[l.__class__.__name__] = rex_game.get(l.__class__.__name__, 0) + 1
if not LOG_GOOD_ONLY and LOG_BAD_GAME and not isinstance(l, game.GameLog):
trash_log.info((l.values['log']))
if l and l.values:
trash_log.info((l.values['log']))
else:
print(l)
else:
logging.warning('No game log in %s' % logf.idstr)
if logf.chat_log:
@ -136,7 +139,10 @@ if __name__ == '__main__':
if not l.unpack() or COUNT_GOOD:
rex_chat[l.__class__.__name__] = rex_chat.get(l.__class__.__name__, 0) + 1
if not LOG_GOOD_ONLY and LOG_BAD_CHAT and not isinstance(l, chat.ChatLog):
trash_log.info((l.values['log']))
if l and l.values:
trash_log.info((l.values['log']))
else:
print(l)
else:
logging.warning('No chat log in %s' % logf.idstr)

View File

@ -3,18 +3,29 @@
todo: finding battles. factory for missions, skirmishes?
"""
from scon.logs import game, combat
# basic battle: responsible for managing, recognizing and parsing a single battle instance.
class Battle(object):
def __init__(self, parent=None):
# parent is a log-session usually
_game_type_strings = []
@classmethod
def is_my_gametype(cls, gametype=None, level=None):
if gametype:
if gametype in cls._game_type_strings:
return True
def __init__(self, parent=None, gametype=None, level=None):
# parent is a log-session
self.parent = parent
self.players = []
self.teams = []
self.time_start = None
self.time_end = None
self.owner = None
self.live = False # whether this is a streamed object.
self.map = None
self.level = level or ''
self.gametype = gametype or ''
def parse_details(self):
# fast parse strategy: fill in all details about this battle.
@ -25,25 +36,25 @@ class Battle(object):
pass
class PvPBattle(Battle):
pass
_game_type_strings = ['BombTheBase', 'Control', 'KingOfTheHill', 'CaptureTheBase', 'TeamDeathMatch', 'GreedyTeamDeathMatch', 'Sentinel']
class PvPTDM(PvPBattle):
pass
_game_type_strings = ['TeamDeathMatch', 'GreedyTeamDeathMatch' ]
class PvPDomination(PvPBattle):
pass
_game_type_strings = ['Control']
class PvPCombatRecon(PvPBattle):
pass
_game_type_strings = ['Sentinel']
class PvPCtB(PvPBattle):
pass
_game_type_strings = ['CaptureTheBase']
class PvPDetonation(PvPBattle):
pass
_game_type_strings = ['BombTheBase']
class PvPBeaconHunt(PvPBattle):
pass
_game_type_strings = ['KingOfTheHill']
# Dreads
class DreadnoughtBattle(Battle):
@ -51,27 +62,68 @@ class DreadnoughtBattle(Battle):
### PvE Stuff: low prio.
class PvEBattle(Battle):
pass
_game_type_strings = ['PVE_Mission',]
class PvERaidBattle(PvEBattle):
pass
# Openspace time.
class Openspace(Battle):
_game_type_strings = ['FreeSpace']
pass
class UnknownBattle(Battle):
@classmethod
def is_my_gametype(cls, gametype=None, level=None):
if gametype:
return True
BATTLE_TYPES = [
# here the more detailed ones
PvPTDM, PvPDomination, PvPCombatRecon,
PvPCtB, PvPDetonation, PvPBeaconHunt,
DreadnoughtBattle,
PvEBattle, PvERaidBattle,
# freespace, general pvp battle
Openspace,
PvPBattle,
# unknowns:
UnknownBattle
]
###
def battle_factory(logs):
''' takes a log session and returns the battles in it
makes a preliminary scan for information
'''
if logs.combat_log and logs.game_log:
# without these it does not make sense
# check combat_log
pass
return []
battles = []
battle = None
if logs.game_log:
# check game log
for line in logs.game_log.lines:
if isinstance(line, game.StartingLevel):
if not line.unpack():
print('Encountered broken packet.')
continue
if not line.is_mainmenu():
# this is the beginning of a new battle.
if battle:
battles.append(battle)
bklass = Battle
for klass in BATTLE_TYPES:
if klass.is_my_gametype(line.values.get('gametype', None), line.values.get('level', None)):
bklass = klass
break
if bklass:
battle = bklass(logs, line.values.get('gametype', None), line.values.get('level', None))
else:
battle = None
else:
if battle:
battles.append(battle)
battle = None
return battles

View File

@ -26,6 +26,7 @@ import logging
This is the reason, the base layout of the log object is explained here.
"""
import datetime
L_CMBT = 'CMBT'
L_WARNING = 'WARNING'
@ -33,7 +34,7 @@ L_NET = 'NET' # Not supported in near future.
L_CHAT = 'CHAT'
class Log(object):
__slots__ = ['trash', 'reviewed', '_match_id', 'values']
__slots__ = ['trash', 'reviewed', '_match_id', 'values', '_timestamp']
matcher = None
def __init__(self):
@ -41,11 +42,23 @@ class Log(object):
self.reviewed = False
self.values = None
self._match_id = None
self._timestamp = None
@classmethod
def is_handler(cls, log):
return False
@property
def timestamp(self):
if self._timestamp:
return self._timestamp
# build timestamp:
# datetime.time(hour[, minute[, second[, microsecond[, tzinfo]]]])
_time = datetime.time( int(self.values.get('hh')),
int(self.values.get('mm')),
int(self.values.get('ss'), 0),
int(self.values.get('ns', 0)) )
def unpack(self, force=False):
''' unpacks this log from its data and saves values '''
pass

View File

@ -87,6 +87,7 @@ class GameLog(Log):
self.values.update(m.groupdict())
self._match_id = i
self.reviewed = True
self.trash = False
return True
# unknown?
self.trash = True
@ -176,6 +177,12 @@ class StartingLevel(GameLog):
re.compile(r"^======\sstarting\slevel\:\s'(?P<level>[^']+)'\s+======"),
]
def is_mainmenu(self):
if self.reviewed and self.values:
if 'mainmenu' in self.values.get('level', ''):
return True
return False
@classmethod
def _is_handler(cls, log):
if log.get('log', '').startswith('====== starting'):
@ -192,6 +199,7 @@ class LevelStarted(GameLog):
if log.get('log', '').startswith('====== level'):
return True
return False

View File

@ -2,6 +2,8 @@ import os, io, sys, logging, time
from PyQt5 import QtGui, QtCore, Qt
from scon.logs.session import LogSessionCollector
from scon.logs import game, combat, chat
from scon.game.battle import battle_factory
from scon.config.settings import settings
class SessionTreeView(Qt.QTreeView):
def __init__(self, parent):
@ -44,33 +46,22 @@ class SessionTreeView(Qt.QTreeView):
return
session = self.sessions[signal.data()]
session.parse_files(['game.log'])
info_object = Qt.QStandardItem('game.log - %s' % len(session.game_log.lines))
info_object = Qt.QStandardItem('no games. (game.log has %s lines)' % len(session.game_log.lines))
info_object.setEditable(False)
game_sessions = 0
item.appendRow(info_object)
battles = battle_factory(session)
#
# add all starting events
for line in session.game_log.lines:
if isinstance(line, game.StartingLevel):
line.unpack()
v = line.values
level = v.get('level')
o = Qt.QStandardItem("Level '%s' gametype '%s'" %( level, v.get('gametype', '') ))
if 'mainmenu' not in level:
game_sessions += 1
o.setEditable(False)
info_object.appendRow(o)
info_object.setText('game.log - %s games' % (game_sessions,))
for battle in battles:
o = Qt.QStandardItem("%s (Level '%s', Gametype '%s')" %( battle.__class__.__name__, battle.level, battle.gametype ) )
o.setEditable(False)
info_object.appendRow(o)
if len(battles) > 0:
#session.parse_files(['combat.log', 'chat.log'])
info_object.setText('%s games' % (len(battles),))
return
session.parse_files(['combat.log'])
info_object = Qt.QStandardItem('combat.log - %s' % len(session.combat_log.lines))
info_object.setEditable(False)
item.appendRow(info_object)
#
session.parse_files(['chat.log'])
info_object = Qt.QStandardItem('chat.log - %s' % len(session.chat_log.lines))
info_object.setEditable(False)
item.appendRow(info_object)
except:
import traceback
traceback.print_exc()
@ -91,7 +82,10 @@ class MainWindow(Qt.QWidget):
layout.addWidget(self.tree)
#self.tree.itemClicked.connect(self.onClickItem)
self.tree.load_from_directory(os.path.join(os.path.expanduser('~'), 'Documents', 'My Games', 'sc'))
if os.path.exists(os.path.join(os.path.expanduser('~'), 'Documents', 'My Games', 'sc')):
self.tree.load_from_directory(os.path.join(os.path.expanduser('~'), 'Documents', 'My Games', 'sc'))
else:
self.tree.load_from_directory(settings.get_logs_path())
# or delayed (not good for debug):
@ -133,4 +127,5 @@ def main():
if __name__ == "__main__":
import logging
logging.basicConfig(level=logging.DEBUG)
settings.autodetect()
main()

View File

@ -9,6 +9,7 @@ from scon.logs.logfiles import LogFileResolver as LogFile
from scon.logs import combat, game, chat
from scon.logs.session import LogSessionCollector
from scon.logs.game import ClientInfo
from scon.game.battle import battle_factory
from scon.config.settings import settings
settings.autodetect()
@ -24,6 +25,8 @@ if __name__ == '__main__':
print(('length combat log ', len(logf.combat_log.lines)))
if logf.game_log:
print(('length game log ', len(logf.game_log.lines)))
print(battle_factory(logf))
print ("Cleaning.")
logf.clean()
if logf.combat_log: