Skip to content

Commit f00e9fc

Browse files
Fix stream desync when a non-blocking read hits a partial message
handle_incoming_messages(blocking=False) treated a BlockingIOError raised part-way through read_message() as "no messages", discarding the header and payload bytes already consumed. The next read then parsed bytes from the middle of a message as a length header, which surfaces as "Error while unpacking message (0 bytes) ... Invalid offset" and terminates the bot/hivemind/script. Only the first recv of a message is now non-blocking; once any byte has been consumed the rest of the message is read blocking.
1 parent cc246ed commit f00e9fc

1 file changed

Lines changed: 13 additions & 4 deletions

File tree

‎rlbot/interface.py‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,6 @@ def __del__(self):
7070
def _int_to_bytes(val: int) -> bytes:
7171
return val.to_bytes(2, byteorder="big")
7272

73-
def _read_int(self) -> int:
74-
return int.from_bytes(self._read_exact(2), "big")
75-
7673
def _read_exact(self, n: int) -> bytes:
7774
assert self.socket is not None, "Socket has not been established"
7875

@@ -88,7 +85,19 @@ def _read_exact(self, n: int) -> bytes:
8885
return bytes(buff)
8986

9087
def read_message(self) -> bytes:
91-
size = self._read_int()
88+
assert self.socket is not None, "Socket has not been established"
89+
90+
# Only this first recv may raise BlockingIOError when the socket is non-blocking.
91+
# Once any byte of a message has been consumed, the rest must be read blocking,
92+
# otherwise the consumed bytes are lost and the stream loses its framing.
93+
header = self.socket.recv(2)
94+
if len(header) == 0:
95+
raise EOFError
96+
self.socket.setblocking(True)
97+
98+
if len(header) == 1:
99+
header += self._read_exact(1)
100+
size = int.from_bytes(header, "big")
92101
return self._read_exact(size)
93102

94103
def send_bytes(self, data: bytes):

0 commit comments

Comments
 (0)