mainly more work on examples
but also first logic in somewhat usable battle_factory initial timestamping method (TBD)
This commit is contained in:
parent
a5d353e302
commit
39bc7604bf
@ -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):
|
||||
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):
|
||||
if l and l.values:
|
||||
trash_log.info((l.values['log']))
|
||||
else:
|
||||
print(l)
|
||||
else:
|
||||
logging.warning('No chat log in %s' % logf.idstr)
|
||||
|
||||
|
@ -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
|
||||
'''
|
||||
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)
|
||||
|
||||
if logs.combat_log and logs.game_log:
|
||||
# without these it does not make sense
|
||||
# check combat_log
|
||||
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
|
||||
|
||||
pass
|
||||
return []
|
||||
return battles
|
||||
|
||||
|
@ -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
|
||||
|
@ -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'):
|
||||
@ -195,6 +202,7 @@ class LevelStarted(GameLog):
|
||||
|
||||
|
||||
|
||||
|
||||
GAME_LOGS = [#SteamInitialization,
|
||||
MasterServerSession,
|
||||
ClientInfo,
|
||||
|
@ -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
|
||||
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)
|
||||
info_object.setText('game.log - %s games' % (game_sessions,))
|
||||
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)
|
||||
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()
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user