async def request_async_impl(conn, encoding, a2s_proto, challenge=0, retries=0, ping=None): send_time = time.monotonic() resp_data = await conn.request(a2s_proto.serialize_request(challenge)) recv_time = time.monotonic() # Only set ping on first packet received if retries == 0: ping = recv_time - send_time reader = ByteReader(io.BytesIO(resp_data), endian="<", encoding=encoding) response_type = reader.read_uint8() if response_type == A2S_CHALLENGE_RESPONSE: if retries >= DEFAULT_RETRIES: raise BrokenMessageError( "Server keeps sending challenge responses") challenge = reader.read_uint32() return await request_async_impl(conn, encoding, a2s_proto, challenge, retries + 1, ping) if not a2s_proto.validate_response_type(response_type): raise BrokenMessageError("Invalid response type: " + hex(response_type)) return a2s_proto.deserialize_response(reader, response_type, ping)
def players_impl(address, timeout, encoding, challenge=0, retries=0): resp_data = request(address, b"\x55" + challenge.to_bytes(4, "little"), timeout) reader = ByteReader(io.BytesIO(resp_data), endian="<", encoding=encoding) response_type = reader.read_uint8() if response_type == A2S_CHALLENGE_RESPONSE: if retries >= DEFAULT_RETRIES: raise BrokenMessageError( "Server keeps sending challenge responses") challenge = reader.read_uint32() return players_impl(address, timeout, encoding, challenge, retries + 1) if response_type != A2S_PLAYER_RESPONSE: raise BrokenMessageError("Invalid response type: " + str(response_type)) player_count = reader.read_uint8() resp = [ Player(index=reader.read_uint8(), name=reader.read_cstring(), score=reader.read_int32(), duration=reader.read_float()) for player_num in range(player_count) ] return resp
def players_request(conn, encoding, challenge=0, retries=0): resp_data = conn.request(b"\x55" + challenge.to_bytes(4, "little")) reader = ByteReader(io.BytesIO(resp_data), endian="<", encoding=encoding) response_type = reader.read_uint8() if response_type == A2S_CHALLENGE_RESPONSE: if retries >= DEFAULT_RETRIES: raise BrokenMessageError( "Server keeps sending challenge responses") challenge = reader.read_uint32() return players_request(conn, encoding, challenge, retries + 1) if response_type != A2S_PLAYER_RESPONSE: raise BrokenMessageError("Invalid response type: " + str(response_type)) return reader
async def rules_request_async(conn, encoding, challenge=0, retries=0): resp_data = await conn.request(b"\x56" + challenge.to_bytes(4, "little")) reader = ByteReader(io.BytesIO(resp_data), endian="<", encoding=encoding) if reader.peek(4) == b"\xFF\xFF\xFF\xFF": reader.read(4) response_type = reader.read_uint8() if response_type == A2S_CHALLENGE_RESPONSE: if retries >= DEFAULT_RETRIES: raise BrokenMessageError( "Server keeps sending challenge responses") challenge = reader.read_uint32() return await rules_request_async(conn, encoding, challenge, retries + 1) if response_type != A2S_RULES_RESPONSE: raise BrokenMessageError("Invalid response type: " + str(response_type)) return reader
def info_response(resp_data, ping, encoding): reader = ByteReader(io.BytesIO(resp_data), endian="<", encoding=encoding) response_type = reader.read_uint8() if response_type == A2S_INFO_RESPONSE: resp = parse_source(reader) elif response_type == A2S_INFO_RESPONSE_LEGACY: resp = parse_goldsrc(reader) else: raise BrokenMessageError("Invalid response type: " + str(response_type)) resp.ping = ping return resp
def info(address, timeout=DEFAULT_TIMEOUT, encoding=DEFAULT_ENCODING): send_time = time.monotonic() resp_data = request(address, b"\x54Source Engine Query\0", timeout) recv_time = time.monotonic() reader = ByteReader( io.BytesIO(resp_data), endian="<", encoding=encoding) response_type = reader.read_uint8() if response_type == A2S_INFO_RESPONSE: resp = parse_source(reader) elif response_type == A2S_INFO_RESPONSE_LEGACY: resp = parse_goldsrc(reader) else: raise BrokenMessageError( "Invalid response type: " + str(response_type)) resp.ping = recv_time - send_time return resp
def recv(self): packet = self._socket.recv(65535) header = packet[:4] data = packet[4:] if header == HEADER_SIMPLE: logger.debug("Received single packet: %r", data) return data elif header == HEADER_MULTI: fragments = [decode_fragment(data)] while len(fragments) < fragments[0].fragment_count: packet = self._socket.recv(4096) fragments.append(decode_fragment(packet[4:])) fragments.sort(key=lambda f: f.fragment_id) reassembled = b"".join(fragment.payload for fragment in fragments) logger.debug("Received %s part packet with content: %r", len(fragments), reassembled) return reassembled else: raise BrokenMessageError("Invalid packet header: " + repr(header))