Compare commits

..

3 commits

4 changed files with 28 additions and 12 deletions

View file

@ -6,7 +6,7 @@ typecheck:
uvx ty check uvx ty check
test: test:
uv run pytest -n auto --testmon uv run pytest -n auto --no-testmon
check: lint typecheck test check: lint typecheck test

View file

@ -203,20 +203,34 @@ class IFSession:
buf += chunk buf += chunk
# Check for prompt at end of buffer # Check for prompt at end of buffer
# dfrotz prompt is "\n> " (with trailing space) # dfrotz prompt is always "> " (with trailing space/newline)
text = buf.decode("latin-1").rstrip() raw = buf.decode("latin-1")
has_prompt = (
raw.endswith("> ") or raw.endswith(">\n") or raw.endswith(">\r\n")
)
text = raw.rstrip()
# \n> is always the dfrotz prompt — strip unconditionally
if text.endswith("\n>"): if text.endswith("\n>"):
return text[:-2].rstrip() return text[:-2].rstrip()
if text == ">": if text == ">":
return "" return ""
# bare > (no preceding newline) — only strip when raw confirms prompt
if has_prompt and text.endswith(">"):
return text[:-1].rstrip()
except TimeoutError: except TimeoutError:
pass pass
text = buf.decode("latin-1").rstrip() raw = buf.decode("latin-1")
has_prompt = raw.endswith("> ") or raw.endswith(">\n") or raw.endswith(">\r\n")
text = raw.rstrip()
# \n> is always the dfrotz prompt — strip unconditionally
if text.endswith("\n>"): if text.endswith("\n>"):
return text[:-2].rstrip() return text[:-2].rstrip()
if text == ">": if text == ">":
return "" return ""
# bare > (no preceding newline) — only strip when raw confirms prompt
if has_prompt and text.endswith(">"):
return text[:-1].rstrip()
return text return text

View file

@ -315,7 +315,7 @@ async def shell(
if player.mode == "editor" and player.editor: if player.mode == "editor" and player.editor:
_writer.write(f" {player.editor.cursor + 1}> ") _writer.write(f" {player.editor.cursor + 1}> ")
elif player.mode == "if" and player.if_session: elif player.mode == "if" and player.if_session:
_writer.write("> ") _writer.write("\r\n> ")
else: else:
_writer.write("mud> ") _writer.write("mud> ")
await _writer.drain() await _writer.drain()
@ -340,20 +340,22 @@ async def shell(
elif player.mode == "if" and player.if_session: elif player.mode == "if" and player.if_session:
response = await player.if_session.handle_input(command) response = await player.if_session.handle_input(command)
if response.output: if response.output:
await player.send(response.output) # Ensure output ends with newline
output = response.output
if not output.endswith("\r\n"):
output += "\r\n"
await player.send(output)
# Broadcast to spectators unless it's an escape command # Broadcast to spectators unless it's an escape command
if not command.startswith("::"): if not command.startswith("::"):
spectator_msg = ( spectator_msg = (
f"[{player.name}'s terminal]\r\n" f"[{player.name}'s terminal]\r\n> {command}\r\n{output}"
f"> {command}\r\n"
f"{response.output}"
) )
await broadcast_to_spectators(player, spectator_msg) await broadcast_to_spectators(player, spectator_msg)
if response.done: if response.done:
await player.if_session.stop() await player.if_session.stop()
player.if_session = None player.if_session = None
player.mode_stack.pop() player.mode_stack.pop()
await player.send("you leave the terminal.\r\n") await player.send("\r\nyou leave the terminal.\r\n")
# Notify spectators # Notify spectators
leave_msg = f"{player.name} steps away from the terminal.\r\n" leave_msg = f"{player.name} steps away from the terminal.\r\n"
await broadcast_to_spectators(player, leave_msg) await broadcast_to_spectators(player, leave_msg)

View file

@ -52,7 +52,7 @@ async def test_start_spawns_subprocess_and_returns_intro():
mock_process.stdout = AsyncMock() mock_process.stdout = AsyncMock()
mock_process.stdin = AsyncMock() mock_process.stdin = AsyncMock()
# Simulate dfrotz output: intro text followed by ">" prompt # Simulate dfrotz output: intro text followed by "\n>" prompt
intro_bytes = b"Welcome to the story!\nYou are in a room.\n>" intro_bytes = b"Welcome to the story!\nYou are in a room.\n>"
async def read_side_effect(n): async def read_side_effect(n):