Skip to content

Commit 971a117

Browse files
committed
Continued to work on s2gs files
Can now parse: * Player ** id ** race ** stats (everything on the score screen) ** graphs * Game ** speed
1 parent 4682853 commit 971a117

File tree

2 files changed

+105
-20
lines changed

2 files changed

+105
-20
lines changed

sc2reader/objects.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,50 @@ def format(self, format_string):
208208
def __repr__(self):
209209
return str(self)
210210

211+
class Graph():
212+
"""
213+
A class to represent a graph on the score screen
214+
"""
215+
#: Times in seconds on the x-axis of the graph
216+
times = list()
217+
218+
#: Values on the y-axis of the graph
219+
values = list()
220+
221+
def __init__(self, x, y, xy_list=None):
222+
if xy_list:
223+
for x, y in xy_list:
224+
self.times.append(x)
225+
self.values.append(y)
226+
else:
227+
self.times = x
228+
self.values = y
229+
230+
def as_points(self):
231+
""" Get the graph as a list of (x, y) tuples """
232+
return zip(self.times, self.values)
233+
234+
def __str__(self):
235+
return "Graph with {0} values".format(len(self.times))
236+
211237
class PlayerSummary():
212238
"""
213239
A class to represent a player in the game summary (.s2gs)
214240
"""
241+
stats_pretty_names = {
242+
'R' : 'Resources',
243+
'U' : 'Units',
244+
'S' : 'Structures',
245+
'O' : 'Overview',
246+
'AUR' : 'Average Unspent Resources',
247+
'RCR' : 'Resource Collection Rate',
248+
'WC' : 'Workers Created',
249+
'UT' : 'Units Trained',
250+
'KUC' : 'Killed Unit Count',
251+
'SB' : 'Structures Built',
252+
'SRC' : 'Structures Razed Count'
253+
}
254+
215255
#: The index of the player in the game
216256
pid = int()
217257

@@ -230,8 +270,23 @@ class PlayerSummary():
230270
#: unknown2
231271
unknown2 = dict()
232272

273+
#: :class:`Graph` of player army values over time (seconds)
274+
army_graph = None
275+
276+
#: :class:`Graph` of player income over time (seconds)
277+
income_graph = None
278+
279+
#: Stats from the game in a dictionary
280+
stats = dict()
281+
233282
def __init__(self, pid):
234283
self.pid = pid
235284

236285
def __str__(self):
237286
return '{} - {}/{}/'.format(self.race, self.subregion, self.bnetid)
287+
288+
def get_stats(self):
289+
s = ''
290+
for k in self.stats:
291+
s += '{}: {}\n'.format(self.stats_pretty_names[k], self.stats[k])
292+
return s.strip()

sc2reader/resources.py

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212
from sc2reader import utils
1313
from sc2reader import log_utils
14-
from sc2reader.objects import Player, Observer, Team, PlayerSummary
15-
from sc2reader.constants import REGIONS, LOCALIZED_RACES, GAME_SPEED_FACTOR
14+
from sc2reader.objects import Player, Observer, Team, PlayerSummary, Graph
15+
from sc2reader.constants import REGIONS, LOCALIZED_RACES, GAME_SPEED_FACTOR, GAME_SPEED_CODES, RACE_CODES
1616

1717

1818
class Resource(object):
@@ -397,24 +397,26 @@ def read_game_strings(self):
397397
elif parts[0] == 'DocInfo/DescLong':
398398
self.description = parts[1]
399399

400-
s2gsmap = [[4, "Average Unspent Resources"],
401-
[5, "Resource Collection Rate"],
402-
[6, "Workers Created"],
403-
[7, "Units Trained"],
404-
[8, "Killed Unit Count"],
405-
[9, "Structure Built"],
406-
]
407400

408401
class GameSummary(Resource):
409402
url_template = 'http://{0}.depot.battle.net:1119/{1}.s2gs'
410403

411-
race_map = {
412-
'Zerg' : 'Zerg',
413-
'Terr' : 'Terran',
414-
'Prot' : 'Protoss',
415-
'RAND' : 'Random'
416-
}
417-
404+
stats_keys = [
405+
'R',
406+
'U',
407+
'S',
408+
'O',
409+
'AUR',
410+
'RCR',
411+
'WC',
412+
'UT',
413+
'KUC',
414+
'SB',
415+
'SRC',
416+
]
417+
418+
#: Game speed
419+
game_speed = str()
418420

419421
#: Players, a list of :class`PlayerSummary` from the game
420422
players = list()
@@ -428,22 +430,50 @@ def __init__(self, summary_file, filename=None, **options):
428430
part = buffer.read_data_struct()
429431
self.parts.append(part)
430432

431-
#Parse players, 16 is the maximum amount of players
433+
# Parse basic info
434+
self.game_speed = GAME_SPEED_CODES[''.join(reversed(self.parts[0][0][1]))]
435+
436+
# Parse player structs, 16 is the maximum amount of players
432437
for i in range(16):
433438
player = None
434439
# Check if player, break if not
435440
if self.parts[0][3][i][2] == '\x00\x00\x00\x00':
436441
break
437442
player_struct = self.parts[0][3][i]
438443

439-
player = PlayerSummary(i)
440-
player.race = self.race_map[player_struct[2]]
444+
player = PlayerSummary(player_struct[0][0])
445+
player.race = RACE_CODES[''.join(reversed(player_struct[2]))]
441446
player.bnetid = player_struct[0][1][0][3]
442447
player.subregion = player_struct[0][1][0][2]
443448

449+
# int
450+
player.unknown1 = player_struct[0][1][0]
451+
# {0:long1, 1:long2}
452+
# Example:
453+
# { 0: 3405691582L, 1: 11402158793782460416L}
454+
player.unknown2 = player_struct[0][1][1]
455+
444456
self.players.append(player)
445457

446-
458+
# Parse graph and stats stucts, for each player
459+
for p in self.players:
460+
461+
# Graph stuff
462+
xy = [(o[2], o[0]) for o in self.parts[4][0][2][1][p.pid]]
463+
p.army_graph = Graph([], [], xy_list=xy)
464+
465+
xy = [(o[2], o[0]) for o in self.parts[4][0][1][1][p.pid]]
466+
p.income_graph = Graph([], [], xy_list=xy)
467+
468+
# Stats stuff
469+
stats_struct = self.parts[3][0]
470+
# The first group of stats is located in parts[3][0]
471+
for i in range(len(stats_struct)):
472+
p.stats[self.stats_keys[i]] = stats_struct[i][1][p.pid][0][0]
473+
# The last piece of stats is in parts[4][0][0][1]
474+
p.stats[self.stats_keys[len(stats_struct)]] = self.parts[4][0][0][1][p.pid][0][0]
475+
476+
447477
class MapInfo(Resource):
448478
url_template = 'http://{0}.depot.battle.net:1119/{1}.s2mi'
449479

0 commit comments

Comments
 (0)