From f1d0d45ced0ef63d66b8230c4c33f541161d9b2f Mon Sep 17 00:00:00 2001 From: Yannik Schmidt Date: Sun, 27 Jan 2019 17:37:47 +0100 Subject: [PATCH] Insurgency specific code --- insurgencyEvent.py | 82 +++++++++++++++++++++++++++++ insurgencyEventSeries.py | 94 ++++++++++++++++++++++++++++++++++ insurgencyParsing.py | 108 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 insurgencyEvent.py create mode 100644 insurgencyEventSeries.py create mode 100644 insurgencyParsing.py diff --git a/insurgencyEvent.py b/insurgencyEvent.py new file mode 100644 index 0000000..58f20be --- /dev/null +++ b/insurgencyEvent.py @@ -0,0 +1,82 @@ +import Player +from datetime import datetime, timedelta + +NO_TEAM = 0 +OBSERVERS = 1 +SECURITY = 2 +INSURGENT = 3 + +class Event: + def __init__(self,timestamp,_map=None): + self.map = _map + self.timestamp = timestamp + def serialize(self): + raise NotImplementedError() + +class DisconnectEvent(Event): + def __init__(self,player,timestamp,line): + self.timestamp = timestamp + self.player = player + self.string = line + def serialize(self): + return {"etype":"DCE","timestamp":self.timestamp.strftime(),"string":self.string} + +class TeamchangeEvent(Event): + def __init__(self,player,old_team,timestamp,line): + self.timestamp = timestamp + self.player = player + self.old_team = int(old_team) + self.string = line + def serialize(self): + return {"etype":"TCE","timestamp":self.timestamp.strftime(),"string":self.string} + +class ActivePlayersEvent(Event): + def __init__(self,player_str,timestamp): + self.timestamp = timestamp + self.players = [] + self.string = player_str + #print(player_str) + try: + for s in player_str.split(","): + #print(s) + if not s or len(s.split("|"))==1: + continue + steamid = s.split("|")[1] + name = s.split("|")[2] + team = int(s.split("|")[3]) + self.players += [Player.PlayerInRound(steamid,name,team,self.timestamp)] + except IndexError: + print("ERROR: CANNOT PARSE LOGLINE: {}".format(player_str)) + print("WARNING: EVENT WILL BE USED IN INCOMPLETE STATE") + + def serialize(self): + return {"etype":"APE","timestamp":self.timestamp.strftime(),"string":self.string} + +class WinnerInformationEvent(Event): + def __init__(self,winner_side,timestamp,line): + self.timestamp = timestamp + self.winner = winner_side + self.string = line + def serialize(self): + return {"etype":"WIE","timestamp":self.timestamp.strftime(),"string":self.string} + +class MapInformationEvent(Event): + def __init__(self,_map,timestamp,line): + self.timestamp = timestamp + self.map = _map + self.string = line + def serialize(self): + return {"etype":"MIE","timestamp":self.timestamp.strftime(),"string":self.string} + +class MapInformationEvent(Event): + def __init__(self,_map,timestamp,line): + self.timestamp = timestamp + self.map = _map + self.string = line + def serialize(self): + return {"etype":"MIE","timestamp":self.timestamp.strftime(),"string":self.string} + + + + + diff --git a/insurgencyEventSeries.py b/insurgencyEventSeries.py new file mode 100644 index 0000000..d4fa027 --- /dev/null +++ b/insurgencyEventSeries.py @@ -0,0 +1,94 @@ +class EventSeries(list): + def __init__(self): + self.winner_side_cache = None + self.loser_side_cache = None + self.map_cache = None + self.security_cache = dict() + self.insurgent_cache = dict() + + def _cache_teams(self): + for e in self: + if type(e) == ActivePlayersEvent: + # TODO deal with players that are missing without a teamchange or dc event # + for p in e.players: + if p not in self._team_from_id(p.team): + self._team_from_id(p.team).update({p:p.rating}) + else: + tmp_team = list(self._team_from_id(p.team)) + tmp_player = tmp_team[tmp_team.index(p)] + ## Add active time if player was active last event ## + if tmp_player.active: + tmp_player.active_time += e.timestamp - tmp_player.timestamp + tmp_player.timestamp = e.timestamp + tmp_player.active = True + + ## set player.active to false for disconnect or teamchange, it will be set to true at the next event that player is seen in a team ## + elif type(e) == DisconnectEvent: + if e.player in self.security_cache and get_key(self.security_cache,e.player).active: + get_key(self.security_cache,e.player).active_time += e.timestamp - get_key(self.security_cache,e.player).timestamp + get_key(self.security_cache,e.player).active = False + elif e.player in self.insurgent_cache and get_key(self.insurgent_cache,e.player).active: + get_key(self.insurgent_cache,e.player).active_time += e.timestamp - get_key(self.insurgent_cache,e.player).timestamp + get_key(self.insurgent_cache,e.player).active = False + elif type(e) == TeamchangeEvent: + if e.player in self._team_from_id(e.old_team): + get_key(self._team_from_id(e.old_team),e.player).active_time += e.timestamp-get_key(self._team_from_id(e.old_team),e.player).timestamp + get_key(self._team_from_id(e.old_team),e.player).active = False + + def _find_winner(self): + time = "NO_TIME_FOUND" + for e in self: + time = e.timestamp#.strftime("%d-%m-%Y %H:%M:%S") + if type(e) == WinnerInformationEvent: + if self.winner_side_cache != None: + raise Warning("%s | Info: More than one Winner in series, skipping Round."%time) + self.winner_side_cache = int(e.winner) + self.loser_side_cache = ( ( ( int(e.winner) - 2 ) + 1 ) % 2) + 2 #löl + if self.winner_side_cache: + return self.winner_side_cache + else: + raise Warning("%s | Info: No winner found in series, skipping Round."%time) + + def _team_from_id(self,tid): + if tid == OBSERVERS or tid == NO_TEAM: + return dict() + elif tid == SECURITY: + return self.security_cache; + elif tid == INSURGENT: + return self.insurgent_cache; + else: + raise ValueError("TeamID must be 0 - NoTeam, 1 - Observers, 2 - Security or 3 - Insurgent, but was {}".format(tid)) + + def get_duration(self): + key = lambda x: x.timestamp + max_ = max(self,key=key) + min_ = min(self,key=key) + ret = max_.timestamp-min_.timestamp + if ret > timedelta(seconds=60*30): + raise Warning("%s | Info: Round Length was %s, too long, ignoring."%(min_.timestamp,ret)) + if ret < timedelta(seconds=60*3): + raise Warning("%s | Info: Round Length was %s, too short, ignoring."%(min_.timestamp,ret)) + return ret + + def get_starttime(self): + key = lambda x: x.timestamp + return min(self,key=key).timestamp + + def get_winners(self): + if not self.security_cache or not self.insurgent_cache: + self._cache_teams() + self._find_winner() + return self._team_from_id(self.winner_side_cache) + + def get_losers(self): + if not self.security_cache or not self.insurgent_cache: + self._cache_teams() + self._find_winner() + return self._team_from_id(self.loser_side_cache) + + def get_map(self): + if self.map_cache == None: + for e in self: + if type(e) == MapInformationEvent: + self.map_cache = e.map + return self.map_cache diff --git a/insurgencyParsing.py b/insurgencyParsing.py new file mode 100644 index 0000000..8f94ceb --- /dev/null +++ b/insurgencyParsing.py @@ -0,0 +1,108 @@ +from InsurgencyEventSeries import EventSeries +import InsurgencyEvent as Event + +def is_round_end(line): + return "0x42,round_end_active" in line +def is_plugin_output(line): + return "0x42" in line +def is_winner_event(line): + return "0x42,winner" in line +def get_key(dic,key): + tmp = list(dic) + return tmp[tmp.index(key)] + +def parseRoundFromLines(r): + + # get an event series # + es = Event.EventSeries() + for l in r: + if is_plugin_output(l): + e = Event.parse_line_to_event(l) + if e != None: + es += [e] + + # get players with teams # + try: + winners = es.get_winners() + losers = es.get_losers() + except Warning as e: + TS.dirty_rounds += 1 + return None + + # deal with teamchanges # + losers_pop = [] + winners_pop = [] + for p in winners: + if p in losers: + if get_key(losers,p).active_time < get_key(winners,p).active_time: + get_key(winners,p).active_time -= get_key(losers,p).active_time + losers_pop += [p] + else: + get_key(losers,p).active_time -= get_key(winners,p).active_time + winners_pop += [p] + + # we cannot change dict during iteration # + for p in losers_pop: + losers.pop(p) + for p in winners_pop: + winners.pop(p) + + # get ratings if there are any yet # + Storrage.sync_from_database(winners) + Storrage.sync_from_database(losers) + + try: + es.get_duration() + except Warning as e: + TS.dirty_rounds += 1 + return None + return Round.Round(winners,losers,es.get_map(),es.get_duration(),es.get_starttime()) + +def create_event(etype,line,timestamp): + TEAMCHANGE = ["teamchange"] + ACTIVE_PLAYERS = ["ct","dc","round_start_active","round_end_active","tc"] + DISCONNECT = ["disconnect"] + WINNER_INFO = ["winner"] + MAP_INFO = ["mapname"] + IGNORE = ["map_start_active","start","plugin unloaded"] + + if etype in TEAMCHANGE: + player = Player.DummyPlayer(line.split(",")[1]) + old_team = line.split(",")[2] + return TeamchangeEvent(player,old_team,timestamp,line) + + elif etype in ACTIVE_PLAYERS: + return ActivePlayersEvent(line,timestamp) + + elif etype in DISCONNECT: + player = Player.DummyPlayer(line.split(",")[1]) + return DisconnectEvent(player,timestamp,line) + + elif etype in WINNER_INFO: + winner_side = line.split(",")[1] + return WinnerInformationEvent(winner_side,timestamp,line) + + elif etype in MAP_INFO: + return MapInformationEvent(line.split(",")[1],timestamp,line) + + elif etype in IGNORE: + pass + + else: + raise Exception("Cannot create event from logline. (etype was: '{}')".format(etype)) + +def parse_line_to_event(l): + tmp = l.split("0x42,")[1].strip("\n") + etype = tmp.split(",")[0].split("|")[0] + try: + if ": L " in l.split("0x42")[0]: + timestamp = datetime.strptime(l.split(": L ")[1].split(": [")[0],"%m/%d/%Y - %H:%M:%S") + else: + timestamp = datetime.strptime(l.split(": [ints_logging.smx]")[0],"L %m/%d/%Y - %H:%M:%S") + except ValueError: + print(" ---- NO TIME ---- | WARNING: Failed to parse time for event, SKIP") + return None + + event = create_event(etype,tmp,timestamp) + Storrage.save_event(event); + return event