Skip to content

Commit 63e9e41

Browse files
committed
Handle missing BLOCK_END before options
This tends to occur in Battlegrounds games, where options are logged inside a block. Cross-referencing power task lists has shown that the block end is simply omitted.
1 parent 8f446df commit 63e9e41

3 files changed

Lines changed: 38 additions & 1 deletion

File tree

hslog/parser.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,17 @@ def _parse_suboption_packet(self, ts, data):
431431

432432
return packet
433433

434+
def _check_for_options_hack(self, ts):
435+
# Battlegrounds games tend to omit the BLOCK_END just before options start. As
436+
# options will always be on the top level, we can safely close any remaining block
437+
# that is open at this time.
438+
if isinstance(self.current_block, packets.Block):
439+
logging.warning("[%s] Broken option nesting. Working around...", ts)
440+
self.block_end(ts)
441+
assert not isinstance(self.current_block, packets.Block)
442+
434443
def handle_options(self, ts, data):
444+
self._check_for_options_hack(ts)
435445
if data.startswith("id="):
436446
sre = tokens.OPTIONS_ENTITY_RE.match(data)
437447
if not sre:

tests/test_logs.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22
from hearthstone.enums import FormatType, GameType
33

4+
from hslog import packets
45
from hslog.export import EntityTreeExporter, ExporterError, FriendlyPlayerExporter
56
from hslog.packets import TagChange
67

@@ -41,7 +42,9 @@ def test_battlegrounds(parser):
4142
packet_tree = parser.games[0]
4243
exporter = EntityTreeExporter(packet_tree)
4344
game = exporter.export()
45+
assert game.game.setup_done
4446
assert game.game.players[0].name == "BehEh#1355"
47+
assert len([packet for packet in packet_tree if isinstance(packet, packets.Options)]) == 96
4548

4649

4750
@pytest.mark.regression_suite

tests/test_main.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
PlayReq, PlayState, PowerType, State, Step, Zone
99
)
1010

11-
from hslog import LogParser
11+
from hslog import LogParser, packets
1212
from hslog.exceptions import ParsingError
1313
from hslog.export import FriendlyPlayerExporter
1414
from hslog.parser import parse_initial_tag
@@ -416,3 +416,27 @@ def test_sub_spell_battlegrounds():
416416
assert sub_spell_packet.source is None
417417
assert sub_spell_packet.target_count == 0
418418
assert sub_spell_packet.targets == []
419+
420+
421+
def test_options_missing_block_end():
422+
parser = LogParser()
423+
parser.read(StringIO(data.INITIAL_GAME))
424+
425+
parser.read(StringIO(
426+
"D 09:01:05.7959635 GameState.DebugPrintPower() - BLOCK_START BlockType=ATTACK Entity=[entityName=Rat Pack id=2974 zone=PLAY zonePos=2 cardId=CFM_316 player=3] EffectCardId= EffectIndex=1 Target=0 SubOption=-1 \n" # noqa
427+
"D 09:01:05.7959635 GameState.DebugPrintPower() - BLOCK_START BlockType=TRIGGER Entity=[entityName=3ofKindCheckPlayerEnchant id=3319 zone=PLAY zonePos=0 cardId=TB_BaconShop_3ofKindChecke player=3] EffectCardId= EffectIndex=-1 Target=0 SubOption=-1 TriggerKeyword=0\n" # noqa
428+
"D 09:01:05.7959635 GameState.DebugPrintPower() - BLOCK_END\n" # noqa
429+
"D 09:01:05.7959635 GameState.DebugPrintPower() - TAG_CHANGE Entity=BehEh#1355 tag=NUM_OPTIONS_PLAYED_THIS_TURN value=15 \n" # noqa
430+
"D 09:01:05.8620235 GameState.DebugPrintOptions() - id=76\n" # noqa
431+
"D 09:01:05.8620235 GameState.DebugPrintOptions() - option 0 type=END_TURN mainEntity= error=INVALID errorParam=\n" # noqa
432+
))
433+
parser.flush()
434+
435+
packet_tree = parser.games[0]
436+
437+
block_without_end = packet_tree.packets[1]
438+
assert isinstance(block_without_end, packets.Block)
439+
assert block_without_end.ended
440+
441+
options_packet = packet_tree.packets[-1]
442+
assert isinstance(options_packet, packets.Options)

0 commit comments

Comments
 (0)