diff --git a/src/mudlib/if_session.py b/src/mudlib/if_session.py index 1a9d57f..1f7b101 100644 --- a/src/mudlib/if_session.py +++ b/src/mudlib/if_session.py @@ -170,34 +170,38 @@ class IFSession: if not self.process or not self.process.stdout: return "" - output = [] + buf = b"" + idle_timeout = 0.1 # return quickly once data stops flowing + overall_deadline = asyncio.get_event_loop().time() + 5.0 + try: - # Read byte by byte until we see "\n>" while True: - byte = await asyncio.wait_for(self.process.stdout.read(1), timeout=5.0) - if not byte: + remaining = overall_deadline - asyncio.get_event_loop().time() + if remaining <= 0: break - - char = byte.decode("latin-1") - output.append(char) - - # Check if we've hit the prompt - # Prompt is "\n>" or just ">" at start - if len(output) >= 2: - if output[-2] == "\n" and output[-1] == ">": - # Strip the trailing "\n>" - output = output[:-2] - break - elif len(output) == 1 and output[0] == ">": - # Prompt at very start - output = [] + timeout = min(idle_timeout, remaining) + try: + chunk = await asyncio.wait_for( + self.process.stdout.read(1024), timeout=timeout + ) + except TimeoutError: + # No data for idle_timeout - dfrotz is done talking break + if not chunk: + break + buf += chunk + + # Check for prompt at end of buffer + text = buf.decode("latin-1") + if text.endswith("\n>"): + text = text[:-2] + return text.rstrip() + if text == ">": + return "" except TimeoutError: - # If we timeout, return what we got pass - result = "".join(output) - return result.rstrip() + return buf.decode("latin-1").rstrip() async def broadcast_to_spectators(player: "Player", message: str) -> None: