Skip to content

Commit f344637

Browse files
committed
improved docstrings and removed parent dependency for LiveEntities
1 parent 1d49e1b commit f344637

5 files changed

Lines changed: 99 additions & 73 deletions

File tree

example_live_parser.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,26 @@
44
from hslog.live.parser import LiveLogParser
55

66

7-
"""
8-
----------------------------------------------------------------------
9-
LiveLogParser assumes that you"ve configured Power.log to be a symlink
10-
11-
in "SOME_PATH/Hearthstone/Logs" folder:
12-
ln -s Power.log /tmp/hearthstone-redirected.log
13-
14-
this will redirect all data coming into Power.log
15-
so we can access it from a RAM disk
16-
----------------------------------------------------------------------
17-
For better performance make /tmp of type tmpfs (or another location)
7+
def main():
8+
"""
9+
----------------------------------------------------------------------
10+
LiveLogParser assumes that you"ve configured Power.log to be a symlink.
1811
19-
in /etc/fstab add line:
20-
tmpfs /tmp tmpfs nodev,nosuid,size=1G 0 0
12+
In "SOME_PATH/Hearthstone/Logs" folder:
13+
ln -s Power.log /tmp/hearthstone-redirected.log
2114
22-
this will create in-memory storage which is faster then SSD
23-
you need to restart the computer for this to take effect
24-
----------------------------------------------------------------------
25-
"""
15+
This will redirect all data coming into Power.log
16+
so we can access it from a RAM disk.
17+
----------------------------------------------------------------------
18+
For better performance make /tmp of type tmpfs (or another location)
2619
20+
In /etc/fstab add line:
21+
tmpfs /tmp tmpfs nodev,nosuid,size=1G 0 0
2722
28-
def main():
23+
This will create in-memory storage which is faster then SSD.
24+
You need to restart the computer for this to take effect.
25+
----------------------------------------------------------------------
26+
"""
2927
try:
3028
file = "/tmp/hearthstone-redirected.log"
3129
liveParser = LiveLogParser(file)

hslog/live/entities.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@
44

55
class LiveEntity(Entity):
66

7-
def __init__(self, entity_id, parent, **kwargs):
8-
""" Entity requires an ID, store everything else in kwargs """
9-
self.parent = parent
10-
self.game_index = self.parent.parser.games.index(self.parent)
11-
super(LiveEntity, self).__init__(entity_id, **kwargs)
7+
def __init__(self, entity_id):
8+
super(LiveEntity, self).__init__(entity_id)
9+
self._game = None
1210

13-
# push data to an end-point
14-
print(f"GAME {self.game_index} --- ENTITY CREATED:", self)
11+
@property
12+
def game(self):
13+
return self._game
14+
15+
@game.setter
16+
def game(self, value):
17+
self._game = value
18+
if value is not None:
19+
# push data to an end-point
20+
print(f"GAME {self.game} --- ENTITY CREATED:", self)
1521

1622
def tag_change(self, tag, value):
1723
if tag == GameTag.CONTROLLER and not self._initial_controller:
@@ -23,26 +29,23 @@ def tag_change(self, tag, value):
2329

2430
def update_callback(self):
2531
# push data to an end-point
26-
print(f"GAME {self.game_index} --- ENTITY UPDATED:", self)
27-
28-
29-
"""
30-
* Card is called on export from game
31-
* LiveCard replaces Card and inserts update_callback
32-
* The point is to become able to route update events towards an API end-point
33-
"""
32+
print(f"GAME {self.game} --- ENTITY UPDATED:", self)
3433

3534

3635
class LiveCard(Card, LiveEntity):
37-
38-
def __init__(self, entity_id, card_id, parent):
39-
super(LiveCard, self).__init__(
40-
entity_id=entity_id,
41-
card_id=card_id,
42-
parent=parent)
43-
44-
""" if card_id doesn"t change, there"s no need to pass it as the argument.
45-
we can use self.card_id instead as it is set by Card class """
36+
"""
37+
Card is called on export from game
38+
LiveCard replaces Card and inserts update_callback
39+
The point is to become able to route update events towards an API end-point
40+
"""
41+
42+
def __init__(self, entity_id, card_id):
43+
super(LiveCard, self).__init__(entity_id, card_id)
44+
45+
"""
46+
if card_id doesn't change, there's no need to pass it as the argument.
47+
we can use self.card_id instead as it is set by Card class
48+
"""
4649
def reveal(self, card_id, tags):
4750
self.revealed = True
4851
self.card_id = card_id

hslog/live/export.py

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,13 @@
33

44

55
class LiveEntityTreeExporter(EntityTreeExporter):
6+
"""
7+
Inherits EntityTreeExporter to provide Live entities
8+
"""
9+
10+
# game_class = LiveGame
11+
# player_class = LivePlayer
612
card_class = LiveCard
713

814
def __init__(self, packet_tree):
915
super(LiveEntityTreeExporter, self).__init__(packet_tree)
10-
11-
def handle_full_entity(self, packet):
12-
entity_id = packet.entity
13-
14-
# Check if the entity already exists in the game first.
15-
# This prevents creating it twice.
16-
# This can legitimately happen in case of GAME_RESET
17-
if entity_id <= len(self.game.entities):
18-
# That first if check is an optimization to prevent always looping over all of
19-
# the game"s entities every single FULL_ENTITY packet...
20-
# FIXME: Switching to a dict for game.entities would simplify this.
21-
existing_entity = self.game.find_entity_by_id(entity_id)
22-
if existing_entity is not None:
23-
existing_entity.card_id = packet.card_id
24-
existing_entity.tags = dict(packet.tags)
25-
return existing_entity
26-
27-
entity = self.card_class(entity_id, packet.card_id, self.packet_tree)
28-
entity.tags = dict(packet.tags)
29-
self.game.register_entity(entity)
30-
return entity

hslog/live/packets.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ def __init__(self, ts, parser):
1010
super(LivePacketTree, self).__init__(ts)
1111

1212
def live_export(self, packet):
13+
"""
14+
Triggers packet export which will run the proper handler for the packet.
15+
This will also run update_callback for entity being updated by the packet.
16+
"""
1317
return self.liveExporter.export_packet(packet)

hslog/live/parser.py

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@
1010

1111

1212
class LiveLogParser(LogParser):
13+
"""
14+
LiveLogParser adds live log translation into useful data.
15+
16+
Lines are read and pushed into a deque by a separate thread.
17+
Deque is emptied by parse_worker which replaces the read()
18+
function of LogParser and it's also in a separate thread.
19+
20+
This approach is non-blocking and allows for live parsing
21+
of incoming lines.
22+
"""
1323

1424
def __init__(self, filepath):
1525
super(LiveLogParser, self).__init__()
@@ -18,13 +28,27 @@ def __init__(self, filepath):
1828
self.lines_deque = deque([])
1929

2030
def new_packet_tree(self, ts):
31+
"""
32+
LivePacketTree is introduced here because it instantiates LiveEntityTreeExporter
33+
and keeps track of the parser parent. It also contains a function that
34+
utilizes the liveExporter instance across all the games.
35+
36+
self.parser = parser
37+
self.liveExporter = LiveEntityTreeExporter(self)
38+
"""
2139
self._packets = LivePacketTree(ts, self)
2240
self._packets.spectator_mode = self.spectator_mode
2341
self._packets.manager = PlayerManager()
2442
self.current_block = self._packets
2543
self.games.append(self._packets)
2644

27-
""" why is this return important? """
45+
"""
46+
why is this return important?
47+
it's called only here:
48+
49+
def create_game(self, ts):
50+
self.new_packet_tree(ts)
51+
"""
2852
return self._packets
2953

3054
def tag_change(self, ts, e, tag, value, def_change):
@@ -47,26 +71,35 @@ def tag_change(self, ts, e, tag, value, def_change):
4771
return packet
4872

4973
def register_packet(self, packet, node=None):
50-
""" make sure we"re registering packets to the current game"""
74+
"""
75+
LogParser.register_packet override
76+
77+
This uses the live_export functionality introduces by LivePacketTree
78+
It also keeps track of which LivePacketTree is being used when there
79+
are multiple in parser.games
80+
81+
A better naming for a PacketTree/LivePacketTree would be HearthstoneGame?
82+
Then parser.games would contain HearthstoneGame instances and would
83+
be more obvious what the purpose is.
84+
"""
85+
86+
# make sure we're registering packets to the current game
5187
if not self._packets or self._packets != self.games[-1]:
5288
self._packets = self.games[-1]
5389

5490
if node is None:
5591
node = self.current_block.packets
5692
node.append(packet)
57-
58-
""" line below triggers packet export which will
59-
run update_callback for entity being
60-
updated by the packet.
61-
62-
self._packets == EntityTreeExporter
63-
"""
6493
self._packets.live_export(packet)
65-
6694
self._packets._packet_counter += 1
6795
packet.packet_id = self._packets._packet_counter
6896

6997
def file_worker(self):
98+
"""
99+
File reader thread. (Naive implementation)
100+
Reads the log file continuously and appends to deque.
101+
"""
102+
70103
file = open(self.filepath, "r")
71104
while self.running:
72105
line = file.readline()
@@ -76,6 +109,9 @@ def file_worker(self):
76109
time.sleep(0.2)
77110

78111
def parse_worker(self):
112+
"""
113+
If deque contains lines, this initiates parsing.
114+
"""
79115
while self.running:
80116
if len(self.lines_deque):
81117
line = self.lines_deque.popleft()

0 commit comments

Comments
 (0)