Wire IF mode into server shell loop and player model
This commit is contained in:
parent
d210033f33
commit
dc342224b1
3 changed files with 136 additions and 1 deletions
|
|
@ -10,6 +10,7 @@ from mudlib.entity import Entity
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from mudlib.editor import Editor
|
||||
from mudlib.if_session import IFSession
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -22,6 +23,7 @@ class Player(Entity):
|
|||
mode_stack: list[str] = field(default_factory=lambda: ["normal"])
|
||||
caps: ClientCaps = field(default_factory=lambda: ClientCaps(ansi=True))
|
||||
editor: Editor | None = None
|
||||
if_session: IFSession | None = None
|
||||
|
||||
@property
|
||||
def mode(self) -> str:
|
||||
|
|
|
|||
|
|
@ -312,6 +312,8 @@ async def shell(
|
|||
# Show appropriate prompt based on mode
|
||||
if player.mode == "editor" and player.editor:
|
||||
_writer.write(f" {player.editor.cursor + 1}> ")
|
||||
elif player.mode == "if" and player.if_session:
|
||||
_writer.write("> ")
|
||||
else:
|
||||
_writer.write("mud> ")
|
||||
await _writer.drain()
|
||||
|
|
@ -321,7 +323,7 @@ async def shell(
|
|||
break
|
||||
|
||||
command = inp.strip()
|
||||
if not command and player.mode != "editor":
|
||||
if not command and player.mode not in ("editor", "if"):
|
||||
continue
|
||||
|
||||
# Handle editor mode
|
||||
|
|
@ -332,6 +334,16 @@ async def shell(
|
|||
if response.done:
|
||||
player.editor = None
|
||||
player.mode_stack.pop()
|
||||
# Handle IF mode
|
||||
elif player.mode == "if" and player.if_session:
|
||||
response = await player.if_session.handle_input(command)
|
||||
if response.output:
|
||||
await player.send(response.output)
|
||||
if response.done:
|
||||
await player.if_session.stop()
|
||||
player.if_session = None
|
||||
player.mode_stack.pop()
|
||||
await player.send("you leave the terminal.\r\n")
|
||||
else:
|
||||
# Dispatch normal command
|
||||
await mudlib.commands.dispatch(player, command)
|
||||
|
|
|
|||
121
tests/test_if_mode.py
Normal file
121
tests/test_if_mode.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
"""Tests for IF mode integration with player model and server shell."""
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from mudlib.if_session import IFResponse, IFSession
|
||||
from mudlib.player import Player
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_writer():
|
||||
writer = MagicMock()
|
||||
writer.write = MagicMock()
|
||||
writer.drain = AsyncMock()
|
||||
return writer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_reader():
|
||||
return MagicMock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def player(mock_reader, mock_writer):
|
||||
return Player(name="TestPlayer", x=5, y=5, reader=mock_reader, writer=mock_writer)
|
||||
|
||||
|
||||
def test_player_has_if_session_attribute(player):
|
||||
"""Player has if_session attribute that defaults to None."""
|
||||
assert hasattr(player, "if_session")
|
||||
assert player.if_session is None
|
||||
|
||||
|
||||
def test_player_if_session_can_be_set(player):
|
||||
"""Player.if_session can be set to an IFSession instance."""
|
||||
session = IFSession(player, "/path/to/story.z5", "story")
|
||||
player.if_session = session
|
||||
assert player.if_session == session
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_if_mode_routes_input_to_if_session(player):
|
||||
"""When mode is 'if' and if_session is set, input routes to if_session.handle_input."""
|
||||
# Create a mock IF session
|
||||
mock_session = MagicMock()
|
||||
mock_session.handle_input = AsyncMock(
|
||||
return_value=IFResponse(output="You see a room.\r\n", done=False)
|
||||
)
|
||||
|
||||
player.if_session = mock_session
|
||||
player.mode_stack.append("if")
|
||||
|
||||
# Simulate input routing (what shell loop should do)
|
||||
if player.mode == "if" and player.if_session:
|
||||
response = await player.if_session.handle_input("look")
|
||||
assert response.output == "You see a room.\r\n"
|
||||
assert response.done is False
|
||||
|
||||
mock_session.handle_input.assert_called_once_with("look")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_if_mode_done_clears_session_and_pops_mode(player):
|
||||
"""When handle_input returns done=True, mode pops and if_session is cleared."""
|
||||
# Create a mock IF session that returns done=True
|
||||
mock_session = MagicMock()
|
||||
mock_session.handle_input = AsyncMock(
|
||||
return_value=IFResponse(output="Goodbye.\r\n", done=True)
|
||||
)
|
||||
mock_session.stop = AsyncMock()
|
||||
|
||||
player.if_session = mock_session
|
||||
player.mode_stack.append("if")
|
||||
|
||||
# Simulate shell loop handling done response
|
||||
if player.mode == "if" and player.if_session:
|
||||
response = await player.if_session.handle_input("::quit")
|
||||
if response.done:
|
||||
await player.if_session.stop()
|
||||
player.if_session = None
|
||||
player.mode_stack.pop()
|
||||
|
||||
assert player.mode == "normal"
|
||||
assert player.if_session is None
|
||||
mock_session.stop.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mode_stack_push_and_pop_for_if(player):
|
||||
"""Test mode stack mechanics for IF mode."""
|
||||
assert player.mode_stack == ["normal"]
|
||||
assert player.mode == "normal"
|
||||
|
||||
# Enter IF mode
|
||||
player.mode_stack.append("if")
|
||||
assert player.mode == "if"
|
||||
assert player.mode_stack == ["normal", "if"]
|
||||
|
||||
# Exit IF mode
|
||||
player.mode_stack.pop()
|
||||
assert player.mode == "normal"
|
||||
assert player.mode_stack == ["normal"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_empty_input_allowed_in_if_mode(player):
|
||||
"""Test that empty input is allowed in IF mode (some IF games accept blank input)."""
|
||||
# Create a mock IF session
|
||||
mock_session = MagicMock()
|
||||
mock_session.handle_input = AsyncMock(
|
||||
return_value=IFResponse(output="Time passes.\r\n", done=False)
|
||||
)
|
||||
|
||||
player.if_session = mock_session
|
||||
player.mode_stack.append("if")
|
||||
|
||||
# Empty input should still be passed to IF session
|
||||
response = await player.if_session.handle_input("")
|
||||
assert response.output == "Time passes.\r\n"
|
||||
mock_session.handle_input.assert_called_once_with("")
|
||||
Loading…
Reference in a new issue