def test_parse_invalid_command(): # Demo files have a 1072 byte header that isn't needed here data = b'HL2DEMO\x00' + bytes([0] * 1064) header = CommandHeader() header.player = 1 header.tick = 1 header.command = 99 df = DemoFile(data + header.pack()) with pytest.raises(CommandError) as exc: df.parse() assert 'Unrecognized command' in str(exc.value)
def __init__(self, game_id, data): self.current_frame = 0 print("Loading game", game_id) # TODO: Use game_id to determine file #Extract DEM file data d = DemoFile(data) current_round = 0 round_active = False # Internal state classes used by the parser! CAREFUL HERE :O s_players = [] #Callback Functions def tick_start(msg): #If first round has not begun yet, return if not round_active: return # print(msg) # Updates every 8 ticks? # Save from their player state to ours for o_pl, th_pl in zip(self.m_rounds[-1].players, s_players): spotted = th_pl.get_prop('DT_BaseEntity', 'm_bSpotted') pos = int(th_pl.position['x']), int(th_pl.position['y']) o_pl.add_position(pos, spotted) def round_start(msg, msg1): nonlocal round_active nonlocal s_players round_active = True print("Round ", current_round) #If previous round never ended with a round_end event, it was probably not a real round if self.m_rounds and self.m_rounds[-1].m_rid == current_round: del self.m_rounds[-1] self.m_rounds.append(Round(current_round)) #In professional matches, there are sometimes players that have no team or are spectators #Store refrences to the internal state of the parser, so we can grab em again later. s_players = [ p for p in d.entities.players if p.team and p.team.name != 'Spectator' ] s_players = sorted(s_players, key=lambda p: p.team.name) # Generate our players players = [Person(p.name, p.team.name) for p in s_players] # And add to round self.m_rounds[-1].players = players def round_end(event, msg): nonlocal current_round nonlocal round_active round_active = False current_round += 1 def death(event, msg): for idx, key in enumerate(event['event'].keys): if key.name == 'userid': user_id = msg.keys[idx].val_short victim = d.entities.get_by_user_id(user_id) if victim and round_active: # Find player index for p in self.m_rounds[-1].players: if p.m_name == victim.name: pos = int(victim.position['x']), int( victim.position['y']) p.die(pos) #Add callback events (calls function whenever event occurs in file) d.add_callback('tick_start', tick_start) d.add_callback('round_start', round_start) d.add_callback('round_end', round_end) d.add_callback('player_death', death) #create list of rounds self.m_rounds = [] #Begin parseing file (may take a while) d.parse() #Close file print("Finished loading game!")
def seperate(n=num_dashes): for count in range(n): print('-', end='') print() #Open and parse .dem file filename = input('Filename(default=\'match.dem\'): ') if filename == '': filename = 'match.dem' print(f'opening \'{filename}\' as DemoFile \'df\'') data = open(filename, 'rb').read() df = DemoFile(data) print('done reading') print('parsing \'df\'...') df.parse() print('finished') seperate() print() players = dict() for player in df.entities.players: teamname = df.entities.teams[player.team_num].clan if teamname == '': continue if teamname not in players: players[teamname] = [player] else: players[teamname].append(player) #Stat calculations
def get_coordinates(): def m_start(event, msg): result.append("Match start") def r_start(event, msg): for idx, key in enumerate(event['event'].keys): if key.name == 'objective': objective = msg.keys[idx].val_string result.append([objective]) def r_end(event, msg): for idx, key in enumerate(event['event'].keys): if key.name == 'winner': winner = msg.keys[idx].val_byte elif key.name == 'reason': reason = msg.keys[idx].val_byte elif key.name == 'legacy': legacy = msg.keys[idx].val_byte elif key.name == 'message': message = msg.keys[idx].val_string elif key.name == 'player_count': player_count = msg.keys[idx].val_short result.append([message]) def death(event, msg): for idx, key in enumerate(event['event'].keys): if key.name == 'attacker': user_id = msg.keys[idx].val_short attacker = d.entities.get_by_user_id(user_id) elif key.name == 'userid': user_id = msg.keys[idx].val_short victim = d.entities.get_by_user_id(user_id) elif key.name == 'weapon': weapon = msg.keys[idx].val_string elif key.name == 'headshot': headshot = msg.keys[idx].val_bool if attacker and victim: result.append([attacker.position, victim.position]) def remove_warmup(res_list): match_start_idx = len(res_list) - 1 - res_list[::-1].index( "Match start") new_res_list = res_list[match_start_idx:] return new_res_list def create_final_res(res_lst): new_result = remove_warmup(res_lst) res_with_round_num = define_round_num(new_result) res_no_trash = remove_trash(res_with_round_num) return res_no_trash def define_round_num(new_res_list): counter = 1 for i in range(len(new_res_list)): if new_res_list[i] in ct_round_end_msgs: new_res_list[i] = [{ "Round msg": new_res_list[i][0], "round num": counter, "winner team": "CTs win" }] counter += 1 elif new_res_list[i] in t_round_end_msgs: new_res_list[i] = [{ "Round msg": new_res_list[i][0], "round num": counter, "winner team": "Ts win" }] counter += 1 return new_res_list def remove_trash(new_res_list): for el in new_res_list: if el == "Match start" or el == ['BOMB TARGET']: new_res_list.pop(new_res_list.index(el)) return new_res_list def create_lists(new_res_list, att_list, vic_list, r_list, wnr_list, tms_list, cts_lvl_list, ts_lvl_list): round_counter = 1 kill_counter = 0 kills_counter = [] final_winner_list = [] final_team_list = [] idx = 0 rnd_cntr = 1 team_one = "" team_two = "" cts_final_lvls = [] ts_final_lvls = [] for el in new_res_list: if len(el) == 1: round_counter += 1 kills_counter.append(kill_counter) kill_counter = 0 wnr_list.append(el[0].get("winner team", "")) cts_lvl_list.append(el[0].get("ct buy level", "")) ts_lvl_list.append(el[0].get("t buy level", "")) else: att_list.append(el[0]) vic_list.append(el[1]) r_list.append(round_counter) kill_counter += 1 for el in wnr_list: if rnd_cntr == 1: team_one = el if rnd_cntr == 15: team_two = team_one team_one = "" if rnd_cntr < 15: if el == team_one: tms_list.append("Team 1") else: tms_list.append("Team 2") else: if el == team_two: tms_list.append("Team 2") else: tms_list.append("Team 1") rnd_cntr += 1 for el in kills_counter: for i in range(el): final_team_list.append(tms_list[idx]) final_winner_list.append(wnr_list[idx]) cts_final_lvls.append(cts_lvl_list[idx]) ts_final_lvls.append(ts_lvl_list[idx]) idx += 1 return att_list, vic_list, r_list, final_winner_list, final_team_list, cts_final_lvls, ts_final_lvls def split_x_y_z(att_or_vic_list, att_or_vic_x, att_or_vic_y, att_or_vic_z): for el in att_or_vic_list: att_or_vic_x.append(el['x']) att_or_vic_y.append(el['y']) att_or_vic_z.append(el['z']) return att_or_vic_x, att_or_vic_y, att_or_vic_z def create_df_from_res(att_x, att_y, att_z, vic_x, vic_y, vic_z, rnds, wnrs, tms): data_frame = pd.DataFrame({ 'attacker_x': att_x, 'attacker_y': att_y, 'attacker_z': att_z, 'victim_x': vic_x, 'victim_y': vic_y, 'victim_z': vic_z, 'round': rnds, 'wnr_team': wnrs, 'team_num': tms }) return data_frame # points for old dust 2: StartX = -2486; StartY = -1150; EndX = 2127; EndY = 3455; ResX = 1024; ResY = 1024; def pointx_to_resolutionx(xinput, startX=-2486, endX=2127, resX=1024): sizeX = endX - startX if startX < 0: xinput += startX * (-1.0) else: xinput += startX xoutput = float((xinput / abs(sizeX)) * resX) return xoutput def pointy_to_resolutiony(yinput, startY=-1150, endY=3455, resY=1024): sizeY = endY - startY if startY < 0: yinput += startY * (-1.0) else: yinput += startY youtput = float((yinput / abs(sizeY)) * resY) return resY - youtput - 10 # def convert_data(dataframe, list_of_dfs, rnds, wnrs, tms): # new_df = pd.DataFrame() # # Convert the data to radar positions # new_df['att_map_x'] = dataframe['attacker_x'].apply(pointx_to_resolutionx) # new_df['att_map_y'] = dataframe['attacker_y'].apply(pointy_to_resolutiony) # new_df['vic_map_x'] = dataframe['victim_x'].apply(pointx_to_resolutionx) # new_df['vic_map_y'] = dataframe['victim_y'].apply(pointy_to_resolutiony) # new_df['round'] = rnds # new_df['winner_team'] = wnrs # new_df['team_num'] = tms # list_of_dfs.append(new_df) # # return list_of_dfs def convert_data(dataframe, actual_df, rnds, wnrs, tms, cts_lvls, t_lvls): new_df = pd.DataFrame() # Convert the data to radar positions new_df['att_map_x'] = dataframe['attacker_x'].apply( pointx_to_resolutionx) new_df['att_map_y'] = dataframe['attacker_y'].apply( pointy_to_resolutiony) new_df['vic_map_x'] = dataframe['victim_x'].apply( pointx_to_resolutionx) new_df['vic_map_y'] = dataframe['victim_y'].apply( pointy_to_resolutiony) new_df['round'] = rnds new_df['winner_team'] = wnrs new_df['team_num'] = tms new_df['t_buy_level'] = t_lvls new_df['ct_buy_level'] = cts_lvls actual_df.append(new_df) return actual_df # def df_to_csv(list_of_demos, list_of_dfs): # for i in range(len(list_of_demos)): # for j in range(len(list_of_dfs)): # if i == j: # list_of_dfs[j].to_csv('../csv/{}.csv'.format(list_of_demos[i])) def df_to_csv(actual_df, actual_clear_df): actual_df[0].to_csv('../csv/{}.csv'.format(actual_clear_df[0])) def economy(dataframe, res_list): t_buy_level = 0 ct_buy_level = 0 counter = 1 win_streak = 0 prev_winner = '' for i in range(len(res_list)): if len(res_list[i]) == 1: rnd_winner = res_list[i][0].get("winner team") if counter == 1 or counter == 16: win_streak = 0 if rnd_winner: win_streak += 1 ct_buy_level = 1 t_buy_level = 1 if counter == 2 or counter == 17: if rnd_winner == "CTs win": if rnd_winner == prev_winner: win_streak += 1 ct_buy_level = 2.5 t_buy_level = 1.5 else: win_streak = 1 ct_buy_level = 1.5 t_buy_level = 2.5 else: if rnd_winner == prev_winner: win_streak += 1 ct_buy_level = 1.5 t_buy_level = 2.5 else: win_streak = 1 ct_buy_level = 2.5 t_buy_level = 1.5 if counter == 3 or counter == 18: if rnd_winner == "Ts win": if rnd_winner == prev_winner: win_streak += 1 ct_buy_level = 1.5 t_buy_level = 2.7 else: win_streak = 1 ct_buy_level = 2.7 t_buy_level = 1.5 else: if rnd_winner == prev_winner: win_streak += 1 ct_buy_level = 2.7 t_buy_level = 1.5 else: win_streak = 1 ct_buy_level = 1.5 t_buy_level = 2.7 if 3 < counter <= 15 or 18 < counter <= 30: if rnd_winner == "CTs win": if rnd_winner != prev_winner: win_streak = 1 # ct_buy_level += 0.2 t_buy_level -= 0.5 else: if win_streak > 4 and t_buy_level == 2.5: t_buy_level = 3 elif win_streak > 4 and t_buy_level == 3: t_buy_level = 2.5 else: win_streak += 1 if win_streak >= 4: ct_buy_level += 0.7 t_buy_level = 2.5 else: if win_streak > 1 and ct_buy_level < 3: t_buy_level += 0.5 else: ct_buy_level += 0.4 t_buy_level -= 0.5 if t_buy_level < 1: t_buy_level = 1 if ct_buy_level > 5: ct_buy_level = 5 if t_buy_level > 5: t_buy_level = 5 elif rnd_winner == "Ts win": if rnd_winner != prev_winner: win_streak = 1 ct_buy_level -= 0.8 # t_buy_level += 0.5 else: if win_streak > 4 and ct_buy_level == 2.5: ct_buy_level = 3 elif win_streak > 4 and ct_buy_level == 3: ct_buy_level = 2.5 else: win_streak += 1 if win_streak >= 4: ct_buy_level = 2.5 t_buy_level += 0.7 else: if win_streak > 1 and t_buy_level < 3: ct_buy_level += 0.5 else: ct_buy_level -= 0.4 t_buy_level += 0.4 if ct_buy_level < 1: ct_buy_level = 1 if ct_buy_level > 5: ct_buy_level = 5 if t_buy_level > 5: t_buy_level = 5 res_list[i][0]["t buy level"] = round(t_buy_level, 1) res_list[i][0]["ct buy level"] = round(ct_buy_level, 1) res_list[i] = [res_list[i][0]] # res_list[0][i] = [res_list[0][i][0], {"t buy level": round(t_buy_level, 1), "ct buy level": round(ct_buy_level, 1)}] prev_winner = rnd_winner counter += 1 return res_list def all_processes(res): final_res = create_final_res(res) new_final_res = economy(current_df, final_res) attackers_list, victims_list, rounds_list, winner_list, teams_list, ct_lvls, t_lvls = create_lists( final_res, attackers, victims, rounds, winners, teams, ct_final_lvls, t_final_lvls) attackers_x, attackers_y, attackers_z = split_x_y_z( attackers, attacker_x, attacker_y, attacker_z) vitctims_x, victims_y, victims_z = split_x_y_z(victims, victim_x, victim_y, victim_z) df = create_df_from_res(attacker_x, attacker_y, attacker_z, victim_x, victim_y, victim_z, rounds_list, winner_list, teams_list) all_demos_result.append(new_final_res) new_dff = convert_data(df, current_df, rounds_list, winner_list, teams_list, ct_lvls, t_lvls) df_to_csv(current_df, clear_demos_list) return all_demos_result all_demos_result = [] current_df = [] ct_round_end_msgs = [['#SFUI_Notice_Bomb_Defused'], ['#SFUI_Notice_CTs_Win'], ['#SFUI_Notice_Target_Saved']] t_round_end_msgs = [['#SFUI_Notice_Terrorists_Win'], ['#SFUI_Notice_Target_Bombed']] demos_list = [] clear_demos_list = [] for file in os.listdir("../uploads"): if file.endswith(".dem"): demos_list.append(os.path.join("../uploads/", file)) clear_demos_list.append(os.path.splitext(file)[0]) for demo in demos_list: result = [] attackers, victims, rounds, attacker_x, attacker_y, attacker_z, victim_x, victim_y, victim_z, winners, teams, ct_final_lvls, t_final_lvls = [], [], [], [], [], [], [], [], [], [], [], [], [] data = open(demo, 'rb').read() d = DemoFile(data) d.add_callback('round_announce_match_start', m_start) d.add_callback('round_start', r_start) d.add_callback('player_death', death) d.add_callback('round_end', r_end) d.parse() data_res = all_processes(result) return data_res
def death(event, msg): for idx, key in enumerate(event['event'].keys): if key.name == 'attacker': user_id = msg.keys[idx].val_short attacker = d.entities.get_by_user_id(user_id) elif key.name == 'userid': user_id = msg.keys[idx].val_short victim = d.entities.get_by_user_id(user_id) elif key.name == 'weapon': weapon = msg.keys[idx].val_string elif key.name == 'headshot': headshot = msg.keys[idx].val_bool if attacker and victim: print("\n --- Player Death at tick {}---".format(d.current_tick)) print("{} killed by {} with {}. Headshot? {}.\n" "Attacker: health = {} position = {}\n" "Victim: position = {}".format(victim.name, attacker.name, weapon, 'Yes' if headshot else 'No', attacker.health, attacker.position, victim.position)) if __name__ == "__main__": data = open(sys.argv[1], 'rb').read() d = DemoFile(data) d.add_callback('player_death', death) d.parse()