You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

88 lines
2.9 KiB

import io
from a2s.exceptions import BrokenMessageError
from a2s.defaults import DEFAULT_TIMEOUT, DEFAULT_ENCODING, \
DEFAULT_RETRIES
from a2s.a2sstream import A2SStream
from a2s.a2sasync import A2SStreamAsync
from a2s.byteio import ByteReader
A2S_RULES_RESPONSE = 0x45
A2S_CHALLENGE_RESPONSE = 0x41
def rules_response(reader):
rule_count = reader.read_int16()
# Have to use tuples to preserve evaluation order
resp = dict(
(reader.read_cstring(), reader.read_cstring())
for rule_num in range(rule_count)
)
return resp
def rules(address, timeout=DEFAULT_TIMEOUT, encoding=DEFAULT_ENCODING):
conn = A2SStream(address, timeout)
reader = rules_request(conn, encoding)
conn.close()
return rules_response(reader)
def rules_request(conn, encoding, challenge=0, retries=0):
resp_data = conn.request(b"\x56" + challenge.to_bytes(4, "little"))
reader = ByteReader(
io.BytesIO(resp_data), endian="<", encoding=encoding)
# A2S_RESPONSE misteriously seems to add a FF FF FF FF
# long to the beginning of the response which isn't
# mentioned on the wiki.
#
# Behaviour witnessed with TF2 server 94.23.226.200:2045
# As of 2015-11-22, Quake Live servers on steam do not
# Source: valve-python messages.py
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 rules_request(
conn, encoding, challenge, retries + 1)
if response_type != A2S_RULES_RESPONSE:
raise BrokenMessageError(
"Invalid response type: " + str(response_type))
return reader
async def rules(address, timeout=DEFAULT_TIMEOUT, encoding=DEFAULT_ENCODING):
conn = await A2SStreamAsync.create(address, timeout)
reader = await rules_request_async(conn, encoding)
conn.close()
return rules_response(reader)
async def rules_request_async(conn, encoding, challenge=0, retries=0):
resp_data = 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(
conn, encoding, challenge, retries + 1)
if response_type != A2S_RULES_RESPONSE:
raise BrokenMessageError(
"Invalid response type: " + str(response_type))
return reader