Skip to content

Commit ac1e114

Browse files
committed
Sloppy patch for s2gs parsing. Need to verify results.
1 parent 7fead05 commit ac1e114

File tree

2 files changed

+150
-70
lines changed

2 files changed

+150
-70
lines changed

sc2reader/objects.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ def get_stats(self):
291291
s += '{0}: {1}\n'.format(self.stats_pretty_names[k], self.stats[k])
292292
return s.strip()
293293

294+
BuildEntry = namedtuple('BuildEntry',['supply','total_supply','time','order','build_index'])
295+
294296
# TODO: Are there libraries with classes like this in them
295297
class Graph():
296298
"""A class to represent a graph on the score screen."""

sc2reader/resources.py

Lines changed: 148 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from sc2reader.data import builds as datapacks
2121
from sc2reader.events import AbilityEvent, CameraEvent, HotkeyEvent, SelectionEvent
2222
from sc2reader.exceptions import SC2ReaderLocalizationError
23-
from sc2reader.objects import Player, Observer, Team, PlayerSummary, Graph, DepotFile
23+
from sc2reader.objects import Player, Observer, Team, PlayerSummary, Graph, DepotFile, BuildEntry
2424
from sc2reader.constants import REGIONS, LOCALIZED_RACES, GAME_SPEED_FACTOR, LOBBY_PROPERTIES
2525

2626

@@ -731,7 +731,7 @@ def __init__(self, summary_file, filename=None, lang='enUS', **options):
731731
self.winners = list()
732732
self.player = dict()
733733
self.settings = dict()
734-
self.player_stats = defaultdict(dict)
734+
self.player_stats = dict()
735735
self.player_settings = defaultdict(dict)
736736
self.build_orders = defaultdict(list)
737737
self.image_urls = list()
@@ -761,7 +761,7 @@ def __init__(self, summary_file, filename=None, lang='enUS', **options):
761761
self.load_map_info()
762762
self.load_settings()
763763
self.load_player_stats()
764-
self.load_player_builds()
764+
# self.load_player_builds()
765765
self.load_players()
766766

767767
self.game_type = self.settings['Teams'].replace(" ","")
@@ -922,12 +922,155 @@ def use_property(prop, player=None):
922922
value = prop.values[player_setting][0]
923923
self.player_settings[index][name] = translation[(uid, value)]
924924

925+
def load_player_stats(self):
926+
translation = self.translations[self.opt.lang]
927+
928+
stat_items = sum([p[0] for p in self.parts[3:]],[])
929+
930+
for item in stat_items:
931+
# Each stat item is laid out as follows
932+
#
933+
# {
934+
# 0: {0:999, 1:translation_id},
935+
# 1: [ [{p1values},...], [{p2values},...], ...]
936+
# }
937+
stat_id = item[0][1]
938+
if stat_id in translation:
939+
# Assume anything under 1 million is a normal score screen item
940+
# Build order ids are generally 16 million+
941+
if stat_id < 1000000:
942+
stat_name = translation[stat_id]
943+
for pid, value in enumerate(item[1]):
944+
if not value: continue
945+
946+
if stat_name in ('Army Value','Resource Collection Rate','Upgrade Spending','Workers Active'):
947+
# Each point entry for the graph is laid out as follows
948+
#
949+
# {0:Value, 1:0, 2:Time}
950+
#
951+
# The 2nd part of the tuple appears to always be zero and
952+
# the time is in seconds of game time.
953+
xy = [(point[2], point[0]) for point in value]
954+
value = Graph([], [], xy_list=xy)
955+
else:
956+
value = value[0][0]
957+
958+
self.player_stats.setdefault(pid, dict())[stat_name] = value
959+
else:
960+
# Each build item represents one ability and contains
961+
# a list of all the uses of that ability by each player
962+
# up to the first 64 successful actions in the game.
963+
for pindex, commands in enumerate(item[1]):
964+
for command in commands:
965+
self.build_orders[pindex].append(BuildEntry(
966+
supply=command[0],
967+
total_supply=command[1]&0xff,
968+
time=(command[2] >> 8) / 16,
969+
order=stat_name,
970+
build_index=command[1] >> 16
971+
))
972+
else:
973+
print "Echo some sort of issue here for ",stat_id
974+
975+
# Once we've compiled all the build commands we need to make
976+
# sure they are properly sorted for presentation.
977+
for build_order in self.build_orders.values():
978+
build_order.sort(key=lambda x: x.build_index)
979+
980+
def load_players(self):
981+
for index, struct in enumerate(self.parts[0][3]):
982+
if not struct[0][1]: continue # Slot is closed
983+
984+
player = PlayerSummary(struct[0][0])
985+
stats = self.player_stats[index]
986+
settings = self.player_settings[index]
987+
player.is_ai = not isinstance(struct[0][1], dict)
988+
if not player.is_ai:
989+
player.gateway = self.gateway
990+
player.subregion = struct[0][1][0][2]
991+
player.region = REGIONS[player.gateway].get(player.subregion, 'Unknown')
992+
player.bnetid = struct[0][1][0][3]
993+
player.unknown1 = struct[0][1][0]
994+
player.unknown2 = struct[0][1][1]
995+
996+
# Either a referee or a spectator, nothing else to do
997+
if settings['Participant Role'] != 'Participant':
998+
self.observers.append(player)
999+
continue
1000+
1001+
player.play_race = LOBBY_PROPERTIES[0xBB9][1].get(struct[2], None)
1002+
1003+
player.is_winner = isinstance(struct[1],dict) and struct[1][0] == 0
1004+
if player.is_winner:
1005+
self.winners.append(player.pid)
1006+
1007+
team_id = int(settings['Team'].split(' ')[1])
1008+
if team_id not in self.teams:
1009+
self.teams[team_id] = Team(team_id)
1010+
player.team = self.teams[team_id]
1011+
self.teams[team_id].players.append(player)
1012+
1013+
# We can just copy these settings right over
1014+
player.color = utils.Color(name=settings.get('Color', None))
1015+
player.pick_race = settings.get('Race', None)
1016+
player.handicap = settings.get('Handicap', None)
1017+
1018+
# Overview Tab
1019+
player.resource_score = stats.get('Resources', None)
1020+
player.structure_score = stats.get('Structures', None)
1021+
player.unit_score = stats.get('Units', None)
1022+
player.overview_score = stats.get('Overview', None)
1023+
1024+
# Units Tab
1025+
player.units_killed = stats.get('Killed Unit Count', None)
1026+
player.structures_built = stats.get('Structures Built', None)
1027+
player.units_trained = stats.get('Units Trained', None)
1028+
player.structures_razed = stats.get('Structures Razed Count', None)
1029+
1030+
# Graphs Tab
1031+
player.army_graph = stats.get('Army Value')
1032+
player.income_graph = stats.get('Resource Collection Rate', None)
1033+
1034+
# HotS Stats
1035+
# TODO: Add the XP stats?
1036+
# 'Units Produced XP'
1037+
# 'Killed Unit XP'
1038+
# 'Structures Produced XP'
1039+
# 'Structures Razed XP'
1040+
# 'Technology XP'
1041+
player.upgrade_spending_graph = stats.get('Upgrade Spending', None)
1042+
player.workers_active_graph = stats.get('Workers Active', None)
1043+
player.combat_efficiency = stats.get('Combat Efficiency',None)
1044+
player.supply_efficiency = stats.get('Supply Efficiency', None)
1045+
player.apm = stats.get('APM', None)
1046+
1047+
# Economic Breakdown Tab
1048+
if isinstance(player.income_graph, Graph):
1049+
# TODO: Is this algorithm right?
1050+
values = player.income_graph.values
1051+
player.income_rate = sum(values)/len(values)
1052+
else:
1053+
# In old s2gs files the field with this name was actually a number not a graph
1054+
player.income_rate = player.income_graph
1055+
player.income_graph = None
1056+
1057+
player.avg_unspent_resources = stats.get('Average Unspent Resources', None)
1058+
player.workers_created = stats.get('Workers Created', None)
1059+
1060+
# Build Orders Tab
1061+
player.build_order = self.build_orders.get(index, None)
1062+
1063+
self.players.append(player)
1064+
self.player[player.pid] = player
1065+
1066+
"""
9251067
def load_player_stats(self):
9261068
if len(self.parts) < 4: return
9271069
translation = self.translations[self.opt.lang]
9281070
9291071
# Part[3][0][:] and Part[4][0][1] are filled with summary stats
930-
# for the players in the game. Each stat item is laid out as follows
1072+
# for the players in the game.
1073+
# Each stat item is laid out as follows
9311074
#
9321075
# {0: {0:999, 1:translation_id}, 1: [ [{0: Value, 1:0, 2:871???}], [], ...]
9331076
#
@@ -996,72 +1139,7 @@ def load_player_builds(self):
9961139
# sure they are properly sorted for presentation.
9971140
for build_order in self.build_orders.values():
9981141
build_order.sort(key=lambda x: x.build_index)
999-
1000-
def load_players(self):
1001-
for index, struct in enumerate(self.parts[0][3]):
1002-
if not struct[0][1]: continue # Slot is closed
1003-
1004-
player = PlayerSummary(struct[0][0])
1005-
stats = self.player_stats[index]
1006-
settings = self.player_settings[index]
1007-
player.is_ai = not isinstance(struct[0][1], dict)
1008-
if not player.is_ai:
1009-
player.gateway = self.gateway
1010-
player.subregion = struct[0][1][0][2]
1011-
player.region = REGIONS[player.gateway].get(player.subregion, 'Unknown')
1012-
player.bnetid = struct[0][1][0][3]
1013-
player.unknown1 = struct[0][1][0]
1014-
player.unknown2 = struct[0][1][1]
1015-
1016-
# Either a referee or a spectator, nothing else to do
1017-
if settings['Participant Role'] != 'Participant':
1018-
self.observers.append(player)
1019-
continue
1020-
1021-
player.play_race = LOBBY_PROPERTIES[0xBB9][1].get(struct[2], None)
1022-
1023-
player.is_winner = isinstance(struct[1],dict) and struct[1][0] == 0
1024-
if player.is_winner:
1025-
self.winners.append(player.pid)
1026-
1027-
team_id = int(settings['Team'].split(' ')[1])
1028-
if team_id not in self.teams:
1029-
self.teams[team_id] = Team(team_id)
1030-
player.team = self.teams[team_id]
1031-
self.teams[team_id].players.append(player)
1032-
1033-
# We can just copy these settings right over
1034-
# TODO: Get the hex from the color string?
1035-
player.color = utils.Color(name=settings.get('Color', None))
1036-
player.pick_race = settings.get('Race', None)
1037-
player.handicap = settings.get('Handicap', None)
1038-
1039-
# Overview Tab
1040-
player.resource_score = stats.get('Resources', None)
1041-
player.structure_score = stats.get('Structures', None)
1042-
player.unit_score = stats.get('Units', None)
1043-
player.overview_score = stats.get('Overview', None)
1044-
1045-
# Economic Breakdown Tab
1046-
player.avg_unspent_resources = stats.get('Average Unspent Resources', None)
1047-
player.resource_collection_rate = stats.get('Resource Collection Rate', None)
1048-
player.workers_created = stats.get('Workers Created', None)
1049-
1050-
# Units Tab
1051-
player.units_killed = stats.get('Killed Unit Count', None)
1052-
player.structures_built = stats.get('Structures Built', None)
1053-
player.units_trained = stats.get('Units Trained', None)
1054-
player.structures_razed = stats.get('Structures Razed Count', None)
1055-
1056-
# Graphs Tab
1057-
player.army_graph = stats.get('Army Graph')
1058-
player.income_graph = stats.get('Income Graph', None)
1059-
1060-
# Build Orders Tab
1061-
player.build_order = self.build_orders.get(index, None)
1062-
1063-
self.players.append(player)
1064-
self.player[player.pid] = player
1142+
"""
10651143

10661144
def __str__(self):
10671145
return "{0} - {1} {2}".format(self.start_time,self.game_length,

0 commit comments

Comments
 (0)