Skip to content

Commit 01ec670

Browse files
StoicLoofahGraylinKim
authored andcommitted
GameHeart Plugin
1 parent 4dc0d12 commit 01ec670

File tree

5 files changed

+124
-0
lines changed

5 files changed

+124
-0
lines changed

sc2reader/engine/plugins/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
from sc2reader.engine.plugins.apm import APMTracker
55
from sc2reader.engine.plugins.selection import SelectionTracker
66
from sc2reader.engine.plugins.context import ContextLoader
7+
from sc2reader.engine.plugins.gameheart import GameHeartNormalizer

sc2reader/engine/plugins/gameheart.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import, print_function, unicode_literals, division
3+
4+
from sc2reader import objects, utils
5+
6+
7+
def GameHeartNormalizer(replay):
8+
"""
9+
normalize a GameHeart replay to:
10+
1) reset frames to the game start
11+
2) remove observing players
12+
3) fix race selection
13+
4) fix team selection
14+
If a replay is not a GameHeart replay, it should be left untouched
15+
Hopefully, the changes here will also extend to other replays that use
16+
in-game lobbies
17+
18+
This makes a few assumptions
19+
1) 1v1 game
20+
"""
21+
22+
PRIMARY_BUILDINGS = set (['Hatchery', 'Nexus', 'CommandCenter'])
23+
start_frame = -1
24+
actual_players = {}
25+
26+
if not replay.tracker_events:
27+
return replay # necessary using this strategy
28+
29+
for event in replay.tracker_events:
30+
if start_frame != -1 and event.frame > start_frame + 5: # fuzz it a little
31+
break
32+
if event.name == 'UnitBornEvent' and event.control_pid and \
33+
event.unit_type_name in PRIMARY_BUILDINGS:
34+
if event.frame == 0: # it's a normal, legit replay
35+
return replay
36+
start_frame = event.frame
37+
actual_players[event.control_pid] = event.unit.race
38+
39+
# set game length starting with the actual game start
40+
replay.frames -= start_frame
41+
replay.game_length = utils.Length(seconds=replay.frames / 16)
42+
43+
# this should cover events of all types
44+
# not nuking entirely because there are initializations that may be relevant
45+
for event in replay.events:
46+
if event.frame < start_frame:
47+
event.frame = 0
48+
event.second = 0
49+
else:
50+
event.frame -= start_frame
51+
event.second = event.frame >> 4
52+
53+
# replay.humans is okay because they're all still humans
54+
# replay.person and replay.people is okay because the mapping is still true
55+
56+
# add observers
57+
# not reinitializing because players appear to have the properties of observers
58+
# TODO in a better world, these players would get reinitialized
59+
replay.observers += [player for player in replay.players if not player.pid in actual_players]
60+
for observer in replay.observers:
61+
observer.is_observer = True
62+
observer.team_id = None
63+
64+
# reset team
65+
# reset teams
66+
replay.team = {}
67+
replay.teams = []
68+
69+
# reset players
70+
replay.players = [player for player in replay.players if player.pid in actual_players]
71+
for i, player in enumerate(replay.players):
72+
race = actual_players[player.pid]
73+
player.pick_race = race
74+
player.play_race = race
75+
76+
team = objects.Team(i + 1)
77+
team.players.append(player)
78+
team.result = player.result
79+
player.team = team
80+
replay.team[i + 1] = team
81+
replay.teams.append(team)
82+
83+
# set winner
84+
if team.result == 'Win':
85+
replay.winner = team
86+
87+
# clear observers out of the players list
88+
for pid in replay.player.keys():
89+
if not pid in actual_players:
90+
del replay.player[pid]
91+
92+
return replay
89.5 KB
Binary file not shown.
72.1 KB
Binary file not shown.

test_replays/test_all.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,37 @@ def test_factory_plugins(self):
322322
self.assertEqual(result["game_fps"], 16.0)
323323
self.assertTrue(result["is_ladder"])
324324

325+
def test_gameheartnormalizer_plugin(self):
326+
from sc2reader.engine.plugins.gameheart import GameHeartNormalizer
327+
factory = sc2reader.factories.SC2Factory()
328+
factory.register_plugin("Replay", GameHeartNormalizer())
329+
330+
replay = factory.load_replay("test_replays/gameheart/gameheart.SC2Replay")
331+
self.assertEqual(replay.events[0].frame, 0)
332+
self.assertEqual(replay.game_length.seconds, 636)
333+
self.assertEqual(len(replay.observers), 5)
334+
self.assertEqual(replay.players[0].name, 'SjoWBBII')
335+
self.assertEqual(replay.players[0].play_race, 'Terran')
336+
self.assertEqual(replay.players[1].name, 'Stardust')
337+
self.assertEqual(replay.players[1].play_race, 'Protoss')
338+
self.assertEqual(len(replay.teams), 2)
339+
self.assertEqual(replay.teams[0].players[0].name, 'SjoWBBII')
340+
self.assertEqual(replay.teams[1].players[0].name, 'Stardust')
341+
self.assertEqual(replay.winner, replay.teams[1])
342+
343+
replay = factory.load_replay("test_replays/gameheart/gh_sameteam.SC2Replay")
344+
self.assertEqual(replay.events[0].frame, 0)
345+
self.assertEqual(replay.game_length.seconds, 424)
346+
self.assertEqual(len(replay.observers), 5)
347+
self.assertEqual(replay.players[0].name, 'EGJDRC')
348+
self.assertEqual(replay.players[0].play_race, 'Zerg')
349+
self.assertEqual(replay.players[1].name, 'LiquidTaeJa')
350+
self.assertEqual(replay.players[1].play_race, 'Terran')
351+
self.assertEqual(len(replay.teams), 2)
352+
self.assertEqual(replay.teams[0].players[0].name, 'EGJDRC')
353+
self.assertEqual(replay.teams[1].players[0].name, 'LiquidTaeJa')
354+
self.assertEqual(replay.winner, replay.teams[0])
355+
325356

326357
if __name__ == '__main__':
327358
unittest.main()

0 commit comments

Comments
 (0)