diff --git a/src/mudlib/commands/play.py b/src/mudlib/commands/play.py index 100e345..f677aa4 100644 --- a/src/mudlib/commands/play.py +++ b/src/mudlib/commands/play.py @@ -24,7 +24,7 @@ def _find_story(name: str) -> pathlib.Path | None: # prefix match (e.g. "zork" matches "zork1.z3") if _stories_dir.exists(): for path in sorted(_stories_dir.iterdir()): - if path.stem.startswith(name) and path.suffix in _STORY_EXTENSIONS: + if path.stem.lower().startswith(name) and path.suffix in _STORY_EXTENSIONS: return path return None @@ -65,8 +65,8 @@ async def cmd_play(player: Player, args: str) -> None: if not isinstance(story_path, pathlib.Path): story_path = pathlib.Path(story_path) - # Use embedded interpreter for z3 files, dfrotz for others - if story_path.suffix == ".z3": + # Use embedded interpreter for z-machine files, dfrotz for others + if story_path.suffix in (".z3", ".z5", ".z8"): try: session = EmbeddedIFSession(player, str(story_path), game_name) except (FileNotFoundError, OSError) as e: diff --git a/src/mudlib/embedded_if_session.py b/src/mudlib/embedded_if_session.py index 763de68..8a30d90 100644 --- a/src/mudlib/embedded_if_session.py +++ b/src/mudlib/embedded_if_session.py @@ -81,8 +81,10 @@ class EmbeddedIFSession: output = self._screen.flush() if restored: - prefix = "restoring saved game...\r\nrestored." - return f"{prefix}\r\n\r\n{output}" if output else prefix + # After restore, the game processes phantom input (garbage in text + # buffer), producing unwanted output. Suppress it and only show the + # restore confirmation. + return "restoring saved game...\r\nrestored." return output async def handle_input(self, text: str) -> IFResponse: diff --git a/src/mudlib/server.py b/src/mudlib/server.py index 3c9bb0a..0ea0ebf 100644 --- a/src/mudlib/server.py +++ b/src/mudlib/server.py @@ -315,7 +315,8 @@ async def shell( if player.mode == "editor" and player.editor: _writer.write(f" {player.editor.cursor + 1}> ") elif player.mode == "if" and player.if_session: - _writer.write("\r\n> ") + # IF mode: game writes its own prompt, don't add another + pass else: _writer.write("mud> ") await _writer.drain() diff --git a/tests/test_play_command.py b/tests/test_play_command.py index f44946b..f856a33 100644 --- a/tests/test_play_command.py +++ b/tests/test_play_command.py @@ -67,10 +67,9 @@ async def test_play_enters_if_mode(player): mock_session.save_path = Mock(spec=Path) mock_session.save_path.exists = Mock(return_value=False) - with patch("mudlib.commands.play.IFSession") as MockIFSession: - MockIFSession.return_value = mock_session + with patch("mudlib.commands.play.EmbeddedIFSession") as MockSession: + MockSession.return_value = mock_session - # Use .z5 to test dfrotz path with patch("mudlib.commands.play._find_story") as mock_find: mock_find.return_value = "/fake/path/zork1.z5" @@ -108,11 +107,11 @@ async def test_play_handles_dfrotz_missing(player): with patch("mudlib.commands.play.IFSession") as MockIFSession: MockIFSession.return_value = mock_session - # Use .z5 to test dfrotz path + # Use .zblorb to test dfrotz path (z3/z5/z8 go to embedded) with patch("mudlib.commands.play._find_story") as mock_find: - mock_find.return_value = "/fake/path/zork1.z5" + mock_find.return_value = "/fake/path/game.zblorb" - await cmd_play(player, "zork1") + await cmd_play(player, "game") # Verify error message was sent player.writer.write.assert_called() @@ -142,10 +141,9 @@ async def test_play_restores_save_if_exists(player): ) mock_session.start = AsyncMock(return_value=restored_output) - with patch("mudlib.commands.play.IFSession") as MockIFSession: - MockIFSession.return_value = mock_session + with patch("mudlib.commands.play.EmbeddedIFSession") as MockSession: + MockSession.return_value = mock_session - # Use .z5 to test dfrotz path with patch("mudlib.commands.play._find_story") as mock_find: mock_find.return_value = "/fake/path/zork1.z5" @@ -170,14 +168,13 @@ async def test_play_no_restore_if_no_save(player): from mudlib.commands.play import cmd_play - # Mock IFSession + # Mock EmbeddedIFSession mock_session = Mock() mock_session.start = AsyncMock(return_value="Welcome to Zork!") - with patch("mudlib.commands.play.IFSession") as MockIFSession: - MockIFSession.return_value = mock_session + with patch("mudlib.commands.play.EmbeddedIFSession") as MockSession: + MockSession.return_value = mock_session - # Use .z5 to test dfrotz path with patch("mudlib.commands.play._find_story") as mock_find: mock_find.return_value = "/fake/path/zork1.z5"