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.
|
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_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_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_CHAT = True
|
||||||
LOG_BAD_GAME = False
|
LOG_BAD_GAME = True
|
||||||
|
|
||||||
# set up our logging to do our task:
|
# set up our logging to do our task:
|
||||||
FILE_MAIN_LOG = 'scon.log.bak'
|
FILE_MAIN_LOG = 'scon.log.bak'
|
||||||
@ -123,7 +123,10 @@ if __name__ == '__main__':
|
|||||||
if not l.unpack() or COUNT_GOOD:
|
if not l.unpack() or COUNT_GOOD:
|
||||||
rex_game[l.__class__.__name__] = rex_game.get(l.__class__.__name__, 0) + 1
|
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 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:
|
else:
|
||||||
logging.warning('No game log in %s' % logf.idstr)
|
logging.warning('No game log in %s' % logf.idstr)
|
||||||
if logf.chat_log:
|
if logf.chat_log:
|
||||||
@ -136,7 +139,10 @@ if __name__ == '__main__':
|
|||||||
if not l.unpack() or COUNT_GOOD:
|
if not l.unpack() or COUNT_GOOD:
|
||||||
rex_chat[l.__class__.__name__] = rex_chat.get(l.__class__.__name__, 0) + 1
|
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 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:
|
else:
|
||||||
logging.warning('No chat log in %s' % logf.idstr)
|
logging.warning('No chat log in %s' % logf.idstr)
|
||||||
|
|
||||||
|
@ -3,18 +3,29 @@
|
|||||||
|
|
||||||
todo: finding battles. factory for missions, skirmishes?
|
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.
|
# basic battle: responsible for managing, recognizing and parsing a single battle instance.
|
||||||
class Battle(object):
|
class Battle(object):
|
||||||
def __init__(self, parent=None):
|
_game_type_strings = []
|
||||||
# parent is a log-session usually
|
|
||||||
|
@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.players = []
|
||||||
self.teams = []
|
self.teams = []
|
||||||
self.time_start = None
|
self.time_start = None
|
||||||
self.time_end = None
|
self.time_end = None
|
||||||
self.owner = None
|
self.owner = None
|
||||||
self.live = False # whether this is a streamed object.
|
self.live = False # whether this is a streamed object.
|
||||||
self.map = None
|
self.level = level or ''
|
||||||
|
self.gametype = gametype or ''
|
||||||
|
|
||||||
def parse_details(self):
|
def parse_details(self):
|
||||||
# fast parse strategy: fill in all details about this battle.
|
# fast parse strategy: fill in all details about this battle.
|
||||||
@ -25,25 +36,25 @@ class Battle(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class PvPBattle(Battle):
|
class PvPBattle(Battle):
|
||||||
pass
|
_game_type_strings = ['BombTheBase', 'Control', 'KingOfTheHill', 'CaptureTheBase', 'TeamDeathMatch', 'GreedyTeamDeathMatch', 'Sentinel']
|
||||||
|
|
||||||
class PvPTDM(PvPBattle):
|
class PvPTDM(PvPBattle):
|
||||||
pass
|
_game_type_strings = ['TeamDeathMatch', 'GreedyTeamDeathMatch' ]
|
||||||
|
|
||||||
class PvPDomination(PvPBattle):
|
class PvPDomination(PvPBattle):
|
||||||
pass
|
_game_type_strings = ['Control']
|
||||||
|
|
||||||
class PvPCombatRecon(PvPBattle):
|
class PvPCombatRecon(PvPBattle):
|
||||||
pass
|
_game_type_strings = ['Sentinel']
|
||||||
|
|
||||||
class PvPCtB(PvPBattle):
|
class PvPCtB(PvPBattle):
|
||||||
pass
|
_game_type_strings = ['CaptureTheBase']
|
||||||
|
|
||||||
class PvPDetonation(PvPBattle):
|
class PvPDetonation(PvPBattle):
|
||||||
pass
|
_game_type_strings = ['BombTheBase']
|
||||||
|
|
||||||
class PvPBeaconHunt(PvPBattle):
|
class PvPBeaconHunt(PvPBattle):
|
||||||
pass
|
_game_type_strings = ['KingOfTheHill']
|
||||||
|
|
||||||
# Dreads
|
# Dreads
|
||||||
class DreadnoughtBattle(Battle):
|
class DreadnoughtBattle(Battle):
|
||||||
@ -51,27 +62,68 @@ class DreadnoughtBattle(Battle):
|
|||||||
|
|
||||||
### PvE Stuff: low prio.
|
### PvE Stuff: low prio.
|
||||||
class PvEBattle(Battle):
|
class PvEBattle(Battle):
|
||||||
pass
|
_game_type_strings = ['PVE_Mission',]
|
||||||
|
|
||||||
class PvERaidBattle(PvEBattle):
|
class PvERaidBattle(PvEBattle):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Openspace time.
|
# Openspace time.
|
||||||
class Openspace(Battle):
|
class Openspace(Battle):
|
||||||
|
_game_type_strings = ['FreeSpace']
|
||||||
pass
|
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):
|
def battle_factory(logs):
|
||||||
''' takes a log session and returns the battles in it
|
''' takes a log session and returns the battles in it
|
||||||
makes a preliminary scan for information
|
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:
|
bklass = Battle
|
||||||
# without these it does not make sense
|
for klass in BATTLE_TYPES:
|
||||||
# check combat_log
|
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 battles
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
@ -26,6 +26,7 @@ import logging
|
|||||||
This is the reason, the base layout of the log object is explained here.
|
This is the reason, the base layout of the log object is explained here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
L_CMBT = 'CMBT'
|
L_CMBT = 'CMBT'
|
||||||
L_WARNING = 'WARNING'
|
L_WARNING = 'WARNING'
|
||||||
@ -33,7 +34,7 @@ L_NET = 'NET' # Not supported in near future.
|
|||||||
L_CHAT = 'CHAT'
|
L_CHAT = 'CHAT'
|
||||||
|
|
||||||
class Log(object):
|
class Log(object):
|
||||||
__slots__ = ['trash', 'reviewed', '_match_id', 'values']
|
__slots__ = ['trash', 'reviewed', '_match_id', 'values', '_timestamp']
|
||||||
matcher = None
|
matcher = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -41,11 +42,23 @@ class Log(object):
|
|||||||
self.reviewed = False
|
self.reviewed = False
|
||||||
self.values = None
|
self.values = None
|
||||||
self._match_id = None
|
self._match_id = None
|
||||||
|
self._timestamp = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_handler(cls, log):
|
def is_handler(cls, log):
|
||||||
return False
|
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):
|
def unpack(self, force=False):
|
||||||
''' unpacks this log from its data and saves values '''
|
''' unpacks this log from its data and saves values '''
|
||||||
pass
|
pass
|
||||||
|
@ -87,6 +87,7 @@ class GameLog(Log):
|
|||||||
self.values.update(m.groupdict())
|
self.values.update(m.groupdict())
|
||||||
self._match_id = i
|
self._match_id = i
|
||||||
self.reviewed = True
|
self.reviewed = True
|
||||||
|
self.trash = False
|
||||||
return True
|
return True
|
||||||
# unknown?
|
# unknown?
|
||||||
self.trash = True
|
self.trash = True
|
||||||
@ -176,6 +177,12 @@ class StartingLevel(GameLog):
|
|||||||
re.compile(r"^======\sstarting\slevel\:\s'(?P<level>[^']+)'\s+======"),
|
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
|
@classmethod
|
||||||
def _is_handler(cls, log):
|
def _is_handler(cls, log):
|
||||||
if log.get('log', '').startswith('====== starting'):
|
if log.get('log', '').startswith('====== starting'):
|
||||||
@ -195,6 +202,7 @@ class LevelStarted(GameLog):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GAME_LOGS = [#SteamInitialization,
|
GAME_LOGS = [#SteamInitialization,
|
||||||
MasterServerSession,
|
MasterServerSession,
|
||||||
ClientInfo,
|
ClientInfo,
|
||||||
|
@ -2,6 +2,8 @@ import os, io, sys, logging, time
|
|||||||
from PyQt5 import QtGui, QtCore, Qt
|
from PyQt5 import QtGui, QtCore, Qt
|
||||||
from scon.logs.session import LogSessionCollector
|
from scon.logs.session import LogSessionCollector
|
||||||
from scon.logs import game, combat, chat
|
from scon.logs import game, combat, chat
|
||||||
|
from scon.game.battle import battle_factory
|
||||||
|
from scon.config.settings import settings
|
||||||
|
|
||||||
class SessionTreeView(Qt.QTreeView):
|
class SessionTreeView(Qt.QTreeView):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
@ -44,33 +46,22 @@ class SessionTreeView(Qt.QTreeView):
|
|||||||
return
|
return
|
||||||
session = self.sessions[signal.data()]
|
session = self.sessions[signal.data()]
|
||||||
session.parse_files(['game.log'])
|
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)
|
info_object.setEditable(False)
|
||||||
game_sessions = 0
|
|
||||||
item.appendRow(info_object)
|
item.appendRow(info_object)
|
||||||
|
|
||||||
|
battles = battle_factory(session)
|
||||||
#
|
#
|
||||||
# add all starting events
|
# add all starting events
|
||||||
for line in session.game_log.lines:
|
for battle in battles:
|
||||||
if isinstance(line, game.StartingLevel):
|
o = Qt.QStandardItem("%s (Level '%s', Gametype '%s')" %( battle.__class__.__name__, battle.level, battle.gametype ) )
|
||||||
line.unpack()
|
o.setEditable(False)
|
||||||
v = line.values
|
info_object.appendRow(o)
|
||||||
level = v.get('level')
|
if len(battles) > 0:
|
||||||
o = Qt.QStandardItem("Level '%s' gametype '%s'" %( level, v.get('gametype', '') ))
|
#session.parse_files(['combat.log', 'chat.log'])
|
||||||
if 'mainmenu' not in level:
|
info_object.setText('%s games' % (len(battles),))
|
||||||
game_sessions += 1
|
|
||||||
o.setEditable(False)
|
|
||||||
info_object.appendRow(o)
|
|
||||||
info_object.setText('game.log - %s games' % (game_sessions,))
|
|
||||||
return
|
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:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@ -91,7 +82,10 @@ class MainWindow(Qt.QWidget):
|
|||||||
layout.addWidget(self.tree)
|
layout.addWidget(self.tree)
|
||||||
|
|
||||||
#self.tree.itemClicked.connect(self.onClickItem)
|
#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):
|
# or delayed (not good for debug):
|
||||||
|
|
||||||
@ -133,4 +127,5 @@ def main():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import logging
|
import logging
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
settings.autodetect()
|
||||||
main()
|
main()
|
@ -9,6 +9,7 @@ from scon.logs.logfiles import LogFileResolver as LogFile
|
|||||||
from scon.logs import combat, game, chat
|
from scon.logs import combat, game, chat
|
||||||
from scon.logs.session import LogSessionCollector
|
from scon.logs.session import LogSessionCollector
|
||||||
from scon.logs.game import ClientInfo
|
from scon.logs.game import ClientInfo
|
||||||
|
from scon.game.battle import battle_factory
|
||||||
from scon.config.settings import settings
|
from scon.config.settings import settings
|
||||||
|
|
||||||
settings.autodetect()
|
settings.autodetect()
|
||||||
@ -24,6 +25,8 @@ if __name__ == '__main__':
|
|||||||
print(('length combat log ', len(logf.combat_log.lines)))
|
print(('length combat log ', len(logf.combat_log.lines)))
|
||||||
if logf.game_log:
|
if logf.game_log:
|
||||||
print(('length game log ', len(logf.game_log.lines)))
|
print(('length game log ', len(logf.game_log.lines)))
|
||||||
|
print(battle_factory(logf))
|
||||||
|
|
||||||
print ("Cleaning.")
|
print ("Cleaning.")
|
||||||
logf.clean()
|
logf.clean()
|
||||||
if logf.combat_log:
|
if logf.combat_log:
|
||||||
|
Loading…
Reference in New Issue
Block a user