Fix IF bugs: case-insensitive story lookup, double prompt, phantom restore command
- _find_story() now compares path.stem.lower() so "lostpig" matches "LostPig.z8" - Server no longer writes its own prompt in IF mode (game handles prompting) - Suppress phantom game output on restore (saved PC past sread causes garbage) - Route .z5/.z8 files to EmbeddedIFSession now that V5+ is supported
This commit is contained in:
parent
14816478aa
commit
602da45ac2
4 changed files with 19 additions and 19 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue