opengsq-python icon indicating copy to clipboard operation
opengsq-python copied to clipboard

RakNet times out when querying any hostname on Windows

Open thegamecracks opened this issue 2 months ago • 5 comments

When trying to pass a domain name like mc.advancius.net on Windows to the RakNet protocol, the library does not receive any response and times out. This can be reproduced with the example main function in raknet.py:

https://github.com/opengsq/opengsq-python/blob/085a9b66317e63069585cfbeeca954025b062c63/opengsq/protocols/raknet.py#L80-L88

When running this module, the following traceback is printed:

(opengsq-python) C:\Users\home\Documents\GitHub\opengsq-python>py -m opengsq.protocols.raknet
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\tasks.py", line 488, in wait_for
    return await fut
           ^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\queues.py", line 186, in get
    await getter
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\home\Documents\GitHub\opengsq-python\opengsq\protocols\raknet.py", line 88, in <module>
    asyncio.run(main_async())
    ~~~~~~~~~~~^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\runners.py", line 204, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\runners.py", line 127, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\base_events.py", line 719, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\home\Documents\GitHub\opengsq-python\opengsq\protocols\raknet.py", line 85, in main_async
    status = await raknet.get_status()
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\Documents\GitHub\opengsq-python\opengsq\protocols\raknet.py", line 36, in get_status
    response = await UdpClient.communicate(self, request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\Documents\GitHub\opengsq-python\opengsq\protocol_socket.py", line 165, in communicate
    return await protocol_instance.recv()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\Documents\GitHub\opengsq-python\opengsq\protocol_socket.py", line 24, in recv
    return await asyncio.wait_for(self.__packets.get(), timeout=self.__timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\tasks.py", line 487, in wait_for
    async with timeouts.timeout(timeout):
               ~~~~~~~~~~~~~~~~^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\timeouts.py", line 114, in __aexit__
    raise TimeoutError from exc_val
TimeoutError

But when I tested this on a Linux system, this issue was not present:

~$ uv run --with opengsq -m opengsq.protocols.raknet
Installed 11 packages in 8ms
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Status(edition='MCPE', motd_line1='Advancius Network', protocol_version=844, version_name='1.21.111', num_players=161, max_players=500, server_unique_id='12947326039399818065', motd_line2='www.advancius.net', game_mode='Survival', game_mode_numeric=1, port_ipv4=19132, port_ipv6=19132)

And if the IP address of mc.advancius.net is explicitly given (currently 142.44.218.185), it works fine on Windows:

# main.py
import asyncio
from opengsq import RakNet

async def main_async():
    raknet = RakNet(host="142.44.218.185", port=19132, timeout=5.0)
    status = await raknet.get_status()
    print(status)

asyncio.run(main_async())
(opengsq-python) C:\Users\home\Documents\GitHub\opengsq-python>py main.py
Status(edition='MCPE', motd_line1='Advancius Network', protocol_version=844, version_name='1.21.111', num_players=158, max_players=500, server_unique_id='12947326039399818065', motd_line2='www.advancius.net', game_mode='Survival', game_mode_numeric=1, port_ipv4=19132, port_ipv6=19132)

From what I can tell after an hour of debugging, there's a quirk with the UdpClient.communicate() method which ignores the default Socket transport and always passes the local_addr= argument to loop.create_datagram_endpoint(), even when the static method is passed source_port=None and protocol._allow_broadcast is False:

https://github.com/opengsq/opengsq-python/blob/085a9b66317e63069585cfbeeca954025b062c63/opengsq/protocol_socket.py#L148-L167

If I change the source code to pass the remote_addr argument exclusively, then the hostname query works on Windows:

# opengsq/protocol_socket.py
transport, protocol_instance = await loop.create_datagram_endpoint(
    lambda: Socket.Protocol(protocol._timeout),  # Use public Protocol class
-   local_addr=('0.0.0.0', source_port if source_port else 0),
+   remote_addr=(protocol._host, protocol._port),
    allow_broadcast=protocol._allow_broadcast
)

try:
-   transport.sendto(data, (protocol._host, protocol._port))
+   transport.sendto(data)
    return await protocol_instance.recv()
finally:
    transport.close()
(opengsq-python) C:\Users\home\Documents\GitHub\opengsq-python>py -m opengsq.protocols.raknet
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Status(edition='MCPE', motd_line1='Advancius Network', protocol_version=844, version_name='1.21.111', num_players=162, max_players=500, server_unique_id='12947326039399818065', motd_line2='www.advancius.net', game_mode='Survival', game_mode_numeric=1, port_ipv4=19132, port_ipv6=19132)

thegamecracks avatar Oct 27 '25 15:10 thegamecracks

This is indeed a very weird behavior. Why should the hostname not be resolved in windows.

I got some questions for replicating the issue:

  1. Is the python version identical on windows and Linux?
  2. is the host you try to query publicly available for replicating the issue?

Hornochs avatar Oct 28 '25 20:10 Hornochs

Hmm, the Python versions are indeed different - 3.14.0 on Windows and system 3.12.3 on Ubuntu 24.04. mc.advancius.net I'd assume is publicly available, as it is part of the repository.

I was able to repro this issue from 3.9-3.14 on Windows using CPython provided by the official pymanager, alongside uv to create the virtual environments:

pymanager install 3.9 3.10. 3.11 3.12 3.13 3.14  # previously installed
uv run --with opengsq==3.5.0 --python 3.9.13  -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.10.11 -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.11.9  -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.12.10 -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.13.9  -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.14.0  -m opengsq.protocols.raknet
Full output, including tracebacks
C:\Users\home\Downloads>uv run --with opengsq==3.5.0 --python 3.9.13 -m opengsq.protocols.raknet
C:\Users\home\AppData\Local\Python\pythoncore-3.9-64\lib\runpy.py:127: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.9-64\lib\asyncio\queues.py", line 166, in get
    await getter
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.9-64\lib\asyncio\tasks.py", line 490, in wait_for
    return fut.result()
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.9-64\lib\runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.9-64\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\O2nTfdq5-isIIdvTkLGW-\Lib\site-packages\opengsq\protocols\raknet.py", line 88, in <module>
    asyncio.run(main_async())
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.9-64\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.9-64\lib\asyncio\base_events.py", line 647, in run_until_complete
    return future.result()
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\O2nTfdq5-isIIdvTkLGW-\Lib\site-packages\opengsq\protocols\raknet.py", line 85, in main_async
    status = await raknet.get_status()
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\O2nTfdq5-isIIdvTkLGW-\Lib\site-packages\opengsq\protocols\raknet.py", line 36, in get_status
    response = await UdpClient.communicate(self, request)
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\O2nTfdq5-isIIdvTkLGW-\Lib\site-packages\opengsq\protocol_socket.py", line 165, in communicate
    return await protocol_instance.recv()
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\O2nTfdq5-isIIdvTkLGW-\Lib\site-packages\opengsq\protocol_socket.py", line 24, in recv
    return await asyncio.wait_for(self.__packets.get(), timeout=self.__timeout)
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.9-64\lib\asyncio\tasks.py", line 492, in wait_for
    raise exceptions.TimeoutError() from exc
asyncio.exceptions.TimeoutError

C:\Users\home\Downloads>uv run --with opengsq==3.5.0 --python 3.10.11 -m opengsq.protocols.raknet
C:\Users\home\AppData\Local\Python\pythoncore-3.10-64\lib\runpy.py:126: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.10-64\lib\asyncio\queues.py", line 159, in get
    await getter
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.10-64\lib\asyncio\tasks.py", line 456, in wait_for
    return fut.result()
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.10-64\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.10-64\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\X9UJnEQ5D5pKWhEqPsY_w\Lib\site-packages\opengsq\protocols\raknet.py", line 88, in <module>
    asyncio.run(main_async())
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.10-64\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.10-64\lib\asyncio\base_events.py", line 649, in run_until_complete
    return future.result()
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\X9UJnEQ5D5pKWhEqPsY_w\Lib\site-packages\opengsq\protocols\raknet.py", line 85, in main_async
    status = await raknet.get_status()
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\X9UJnEQ5D5pKWhEqPsY_w\Lib\site-packages\opengsq\protocols\raknet.py", line 36, in get_status
    response = await UdpClient.communicate(self, request)
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\X9UJnEQ5D5pKWhEqPsY_w\Lib\site-packages\opengsq\protocol_socket.py", line 165, in communicate
    return await protocol_instance.recv()
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\X9UJnEQ5D5pKWhEqPsY_w\Lib\site-packages\opengsq\protocol_socket.py", line 24, in recv
    return await asyncio.wait_for(self.__packets.get(), timeout=self.__timeout)
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.10-64\lib\asyncio\tasks.py", line 458, in wait_for
    raise exceptions.TimeoutError() from exc
asyncio.exceptions.TimeoutError

C:\Users\home\Downloads>uv run --with opengsq==3.5.0 --python 3.11.9 -m opengsq.protocols.raknet
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.11-64\Lib\asyncio\tasks.py", line 500, in wait_for
    return fut.result()
           ^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.11-64\Lib\asyncio\queues.py", line 158, in get
    await getter
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\y97CNW7WmGUzobAqTmu5u\Lib\site-packages\opengsq\protocols\raknet.py", line 88, in <module>
    asyncio.run(main_async())
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.11-64\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.11-64\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.11-64\Lib\asyncio\base_events.py", line 654, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\y97CNW7WmGUzobAqTmu5u\Lib\site-packages\opengsq\protocols\raknet.py", line 85, in main_async
    status = await raknet.get_status()
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\y97CNW7WmGUzobAqTmu5u\Lib\site-packages\opengsq\protocols\raknet.py", line 36, in get_status
    response = await UdpClient.communicate(self, request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\y97CNW7WmGUzobAqTmu5u\Lib\site-packages\opengsq\protocol_socket.py", line 165, in communicate
    return await protocol_instance.recv()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\y97CNW7WmGUzobAqTmu5u\Lib\site-packages\opengsq\protocol_socket.py", line 24, in recv
    return await asyncio.wait_for(self.__packets.get(), timeout=self.__timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.11-64\Lib\asyncio\tasks.py", line 502, in wait_for
    raise exceptions.TimeoutError() from exc
TimeoutError

C:\Users\home\Downloads>uv run --with opengsq==3.5.0 --python 3.12.10 -m opengsq.protocols.raknet
C:\Users\home\AppData\Local\uv\cache\archive-v0\-6OHqIarc6vORiCAC8zNd\Lib\site-packages\opengsq\protocols\cod1.py:114: SyntaxWarning: invalid escape sequence '\)'
  CoD1 uses backslash (\) as delimiter between keys and values.
C:\Users\home\AppData\Local\uv\cache\archive-v0\-6OHqIarc6vORiCAC8zNd\Lib\site-packages\opengsq\protocols\cod4.py:114: SyntaxWarning: invalid escape sequence '\)'
  CoD4 uses backslash (\) as delimiter between keys and values.
C:\Users\home\AppData\Local\uv\cache\archive-v0\-6OHqIarc6vORiCAC8zNd\Lib\site-packages\opengsq\protocols\cod5.py:114: SyntaxWarning: invalid escape sequence '\)'
  CoD5 uses backslash (\) as delimiter between keys and values.
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.12-64\Lib\asyncio\tasks.py", line 520, in wait_for
    return await fut
           ^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.12-64\Lib\asyncio\queues.py", line 158, in get
    await getter
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\-6OHqIarc6vORiCAC8zNd\Lib\site-packages\opengsq\protocols\raknet.py", line 88, in <module>
    asyncio.run(main_async())
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.12-64\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.12-64\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.12-64\Lib\asyncio\base_events.py", line 691, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\-6OHqIarc6vORiCAC8zNd\Lib\site-packages\opengsq\protocols\raknet.py", line 85, in main_async
    status = await raknet.get_status()
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\-6OHqIarc6vORiCAC8zNd\Lib\site-packages\opengsq\protocols\raknet.py", line 36, in get_status
    response = await UdpClient.communicate(self, request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\-6OHqIarc6vORiCAC8zNd\Lib\site-packages\opengsq\protocol_socket.py", line 165, in communicate
    return await protocol_instance.recv()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\-6OHqIarc6vORiCAC8zNd\Lib\site-packages\opengsq\protocol_socket.py", line 24, in recv
    return await asyncio.wait_for(self.__packets.get(), timeout=self.__timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.12-64\Lib\asyncio\tasks.py", line 519, in wait_for
    async with timeouts.timeout(timeout):
               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.12-64\Lib\asyncio\timeouts.py", line 115, in __aexit__
    raise TimeoutError from exc_val
TimeoutError

C:\Users\home\Downloads>uv run --with opengsq==3.5.0 --python 3.13.9 -m opengsq.protocols.raknet
C:\Users\home\AppData\Local\uv\cache\archive-v0\byEm03QZ4HZlGf2tdXY-k\Lib\site-packages\opengsq\protocols\cod1.py:114: SyntaxWarning: invalid escape sequence '\)'
  CoD1 uses backslash (\) as delimiter between keys and values.
C:\Users\home\AppData\Local\uv\cache\archive-v0\byEm03QZ4HZlGf2tdXY-k\Lib\site-packages\opengsq\protocols\cod4.py:114: SyntaxWarning: invalid escape sequence '\)'
  CoD4 uses backslash (\) as delimiter between keys and values.
C:\Users\home\AppData\Local\uv\cache\archive-v0\byEm03QZ4HZlGf2tdXY-k\Lib\site-packages\opengsq\protocols\cod5.py:114: SyntaxWarning: invalid escape sequence '\)'
  CoD5 uses backslash (\) as delimiter between keys and values.
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.13-64\Lib\asyncio\tasks.py", line 507, in wait_for
    return await fut
           ^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.13-64\Lib\asyncio\queues.py", line 186, in get
    await getter
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\byEm03QZ4HZlGf2tdXY-k\Lib\site-packages\opengsq\protocols\raknet.py", line 88, in <module>
    asyncio.run(main_async())
    ~~~~~~~~~~~^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.13-64\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.13-64\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.13-64\Lib\asyncio\base_events.py", line 725, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\byEm03QZ4HZlGf2tdXY-k\Lib\site-packages\opengsq\protocols\raknet.py", line 85, in main_async
    status = await raknet.get_status()
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\byEm03QZ4HZlGf2tdXY-k\Lib\site-packages\opengsq\protocols\raknet.py", line 36, in get_status
    response = await UdpClient.communicate(self, request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\byEm03QZ4HZlGf2tdXY-k\Lib\site-packages\opengsq\protocol_socket.py", line 165, in communicate
    return await protocol_instance.recv()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\byEm03QZ4HZlGf2tdXY-k\Lib\site-packages\opengsq\protocol_socket.py", line 24, in recv
    return await asyncio.wait_for(self.__packets.get(), timeout=self.__timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.13-64\Lib\asyncio\tasks.py", line 506, in wait_for
    async with timeouts.timeout(timeout):
               ~~~~~~~~~~~~~~~~^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.13-64\Lib\asyncio\timeouts.py", line 116, in __aexit__
    raise TimeoutError from exc_val
TimeoutError

C:\Users\home\Downloads>uv run --with opengsq==3.5.0 --python 3.14.0 -m opengsq.protocols.raknet
C:\Users\home\AppData\Local\uv\cache\archive-v0\qf1PZV7gGMgn4ggLmH4Tk\Lib\site-packages\opengsq\protocols\cod1.py:114: SyntaxWarning: "\)" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\)"? A raw string is also an option.
  CoD1 uses backslash (\) as delimiter between keys and values.
C:\Users\home\AppData\Local\uv\cache\archive-v0\qf1PZV7gGMgn4ggLmH4Tk\Lib\site-packages\opengsq\protocols\cod4.py:114: SyntaxWarning: "\)" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\)"? A raw string is also an option.
  CoD4 uses backslash (\) as delimiter between keys and values.
C:\Users\home\AppData\Local\uv\cache\archive-v0\qf1PZV7gGMgn4ggLmH4Tk\Lib\site-packages\opengsq\protocols\cod5.py:114: SyntaxWarning: "\)" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\)"? A raw string is also an option.
  CoD5 uses backslash (\) as delimiter between keys and values.
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Traceback (most recent call last):
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\tasks.py", line 488, in wait_for
    return await fut
           ^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\queues.py", line 186, in get
    await getter
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\qf1PZV7gGMgn4ggLmH4Tk\Lib\site-packages\opengsq\protocols\raknet.py", line 88, in <module>
    asyncio.run(main_async())
    ~~~~~~~~~~~^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\runners.py", line 204, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\runners.py", line 127, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\base_events.py", line 719, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\qf1PZV7gGMgn4ggLmH4Tk\Lib\site-packages\opengsq\protocols\raknet.py", line 85, in main_async
    status = await raknet.get_status()
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\qf1PZV7gGMgn4ggLmH4Tk\Lib\site-packages\opengsq\protocols\raknet.py", line 36, in get_status
    response = await UdpClient.communicate(self, request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\qf1PZV7gGMgn4ggLmH4Tk\Lib\site-packages\opengsq\protocol_socket.py", line 165, in communicate
    return await protocol_instance.recv()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\uv\cache\archive-v0\qf1PZV7gGMgn4ggLmH4Tk\Lib\site-packages\opengsq\protocol_socket.py", line 24, in recv
    return await asyncio.wait_for(self.__packets.get(), timeout=self.__timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\tasks.py", line 487, in wait_for
    async with timeouts.timeout(timeout):
               ~~~~~~~~~~~~~~~~^^^^^^^^^
  File "C:\Users\home\AppData\Local\Python\pythoncore-3.14-64\Lib\asyncio\timeouts.py", line 114, in __aexit__
    raise TimeoutError from exc_val
TimeoutError

Here's the same 3.9-3.14 tests succeeding on Ubuntu 24.04, using uv-managed Python installations:

uv python install 3.9 3.10 3.11 3.12 3.13 3.14
uv run --with opengsq==3.5.0 --python 3.9  -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.10 -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.11 -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.12 -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.13 -m opengsq.protocols.raknet
uv run --with opengsq==3.5.0 --python 3.14 -m opengsq.protocols.raknet
Full output
~$ uv python install 3.9 3.10 3.11 3.12 3.13 3.14
Installed 6 versions in 2.98s
 + cpython-3.9.24-linux-x86_64-gnu (python3.9)
 + cpython-3.10.19-linux-x86_64-gnu (python3.10)
 + cpython-3.11.14-linux-x86_64-gnu (python3.11)
 + cpython-3.12.12-linux-x86_64-gnu (python3.12)
 + cpython-3.13.9-linux-x86_64-gnu (python3.13)
 + cpython-3.14.0-linux-x86_64-gnu (python3.14)
~$ uv run --with opengsq==3.5.0 --python 3.9  -m opengsq.protocols.raknet
/home/ministatus/.local/share/uv/python/cpython-3.9.24-linux-x86_64-gnu/lib/python3.9/runpy.py:127: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Status(edition='MCPE', motd_line1='Advancius Network', protocol_version=859, version_name='1.21.120', num_players=169, max_players=500, server_unique_id='8643987271603006626', motd_line2='www.advancius.net', game_mode='Survival', game_mode_numeric=1, port_ipv4=19132, port_ipv6=19132)
~$ uv run --with opengsq==3.5.0 --python 3.10 -m opengsq.protocols.raknet
/home/ministatus/.local/share/uv/python/cpython-3.10.19-linux-x86_64-gnu/lib/python3.10/runpy.py:126: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Status(edition='MCPE', motd_line1='Advancius Network', protocol_version=859, version_name='1.21.120', num_players=169, max_players=500, server_unique_id='8643987271603006626', motd_line2='www.advancius.net', game_mode='Survival', game_mode_numeric=1, port_ipv4=19132, port_ipv6=19132)
~$ uv run --with opengsq==3.5.0 --python 3.11 -m opengsq.protocols.raknet
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Status(edition='MCPE', motd_line1='Advancius Network', protocol_version=859, version_name='1.21.120', num_players=169, max_players=500, server_unique_id='8643987271603006626', motd_line2='www.advancius.net', game_mode='Survival', game_mode_numeric=1, port_ipv4=19132, port_ipv6=19132)
~$ uv run --with opengsq==3.5.0 --python 3.12 -m opengsq.protocols.raknet
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Status(edition='MCPE', motd_line1='Advancius Network', protocol_version=859, version_name='1.21.120', num_players=169, max_players=500, server_unique_id='8643987271603006626', motd_line2='www.advancius.net', game_mode='Survival', game_mode_numeric=1, port_ipv4=19132, port_ipv6=19132)
~$ uv run --with opengsq==3.5.0 --python 3.13 -m opengsq.protocols.raknet
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Status(edition='MCPE', motd_line1='Advancius Network', protocol_version=859, version_name='1.21.120', num_players=169, max_players=500, server_unique_id='8643987271603006626', motd_line2='www.advancius.net', game_mode='Survival', game_mode_numeric=1, port_ipv4=19132, port_ipv6=19132)
~$ uv run --with opengsq==3.5.0 --python 3.14 -m opengsq.protocols.raknet
<frozen runpy>:128: RuntimeWarning: 'opengsq.protocols.raknet' found in sys.modules after import of package 'opengsq.protocols', but prior to execution of 'opengsq.protocols.raknet'; this may result in unpredictable behaviour
Status(edition='MCPE', motd_line1='Advancius Network', protocol_version=859, version_name='1.21.120', num_players=169, max_players=500, server_unique_id='8643987271603006626', motd_line2='www.advancius.net', game_mode='Survival', game_mode_numeric=1, port_ipv4=19132, port_ipv6=19132)

My specific Windows version is Windows-11-10.0.26100-SP0 (py -m platform) if that helps.

thegamecracks avatar Oct 28 '25 21:10 thegamecracks

This is indeed a very weird behavior. Why should the hostname not be resolved in windows.

I was able to accidentally reproduce this same bug while writing my own A2S library, and I think I've distilled it to this MRE, written for Python 3.11:

import asyncio, socket

dest_ipv4 = ("23.137.105.24", 16261)
dest_host = ("play.thegamecracks.xyz", 16261)

request = b"\xff\xff\xff\xffTSource Engine Query\x00\xff\xff\xff\xff"
local_addr = ("0.0.0.0", 0)

async def main():
    await try_request(dest_ipv4, local_addr=None, remote_addr=None)
    await try_request(dest_ipv4, local_addr=local_addr, remote_addr=None)
    await try_request(dest_ipv4, local_addr=None, remote_addr=dest_ipv4)
    await try_request(dest_ipv4, local_addr=local_addr, remote_addr=dest_ipv4)
    await try_request(None, local_addr=None, remote_addr=dest_ipv4)
    await try_request(None, local_addr=local_addr, remote_addr=dest_ipv4)

    await try_request(dest_host, local_addr=None, remote_addr=None)
    await try_request(dest_host, local_addr=local_addr, remote_addr=None)
    await try_request(dest_host, local_addr=None, remote_addr=dest_host)
    await try_request(dest_host, local_addr=local_addr, remote_addr=dest_host)
    await try_request(None, local_addr=None, remote_addr=dest_host)
    await try_request(None, local_addr=local_addr, remote_addr=dest_host)

async def try_request(addr, local_addr, remote_addr):
    print(f"  Sending to {addr}, {local_addr = }, {remote_addr = }")
    loop = asyncio.get_running_loop()

    try:
        transport, protocol = await loop.create_datagram_endpoint(
            MyProtocol,
            local_addr=local_addr,
            remote_addr=remote_addr,
            family=socket.AF_INET,
        )
    except ValueError as e:
        return print(f"    {e}")

    try:
        async with protocol, asyncio.timeout(1):
            transport.sendto(request, addr)
            response = await asyncio.shield(protocol.received_fut)
            print(f"    {response.hex(':')}")
    except TimeoutError:
        print("    Timed out")
    except ValueError as e:
        print(f"    {e}")

    await asyncio.sleep(1)  # avoid spamming requests

class MyProtocol(asyncio.DatagramProtocol):
    def __init__(self):
        loop = asyncio.get_running_loop()
        self.received_fut = loop.create_future()
        self.closed_fut = loop.create_future()

    def connection_made(self, transport):
        self.transport = transport

    def datagram_received(self, data, addr):
        self.received_fut.set_result(data)

    def connection_lost(self, exc) -> None:
        self.closed_fut.set_result(None)

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, tb):
        self.transport.close()
        await asyncio.shield(self.closed_fut)

print("With (implied) protactor event loop:")
asyncio.run(main())

print("With selector event loop:")
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(main())

I included the selector event loop after noticing this in the Python docs: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_datagram_endpoint

local_addr, if given, is a (local_host, local_port) tuple used to bind the socket locally. The local_host and local_port are looked up using getaddrinfo(). Note: On Windows, when using the proactor event loop with local_addr=None, an OSError with errno.WSAEINVAL will be raised when running it.

Anyway, here's the output of that MRE:

Output
With (implied) protactor event loop:
  Sending to ('23.137.105.24', 16261), local_addr = None, remote_addr = None
    Timed out
  Sending to ('23.137.105.24', 16261), local_addr = ('0.0.0.0', 0), remote_addr = None
    ff:ff:ff:ff:41:3e:74:af:c2
  Sending to ('23.137.105.24', 16261), local_addr = None, remote_addr = ('23.137.105.24', 16261)
    ff:ff:ff:ff:41:03:48:60:ef
  Sending to ('23.137.105.24', 16261), local_addr = ('0.0.0.0', 0), remote_addr = ('23.137.105.24', 16261)
    ff:ff:ff:ff:41:03:48:60:ef
  Sending to None, local_addr = None, remote_addr = ('23.137.105.24', 16261)
    ff:ff:ff:ff:41:03:48:60:ef
  Sending to None, local_addr = ('0.0.0.0', 0), remote_addr = ('23.137.105.24', 16261)
    ff:ff:ff:ff:41:03:48:60:ef
  Sending to ('play.thegamecracks.xyz', 16261), local_addr = None, remote_addr = None
    Timed out
  Sending to ('play.thegamecracks.xyz', 16261), local_addr = ('0.0.0.0', 0), remote_addr = None
    Timed out
  Sending to ('play.thegamecracks.xyz', 16261), local_addr = None, remote_addr = ('play.thegamecracks.xyz', 16261)
    Invalid address: must be None or ('23.137.105.24', 16261)
  Sending to ('play.thegamecracks.xyz', 16261), local_addr = ('0.0.0.0', 0), remote_addr = ('play.thegamecracks.xyz', 16261)
    can not get address information
  Sending to None, local_addr = None, remote_addr = ('play.thegamecracks.xyz', 16261)
    ff:ff:ff:ff:41:11:a6:88:7f
  Sending to None, local_addr = ('0.0.0.0', 0), remote_addr = ('play.thegamecracks.xyz', 16261)
    can not get address information
With selector event loop:
  Sending to ('23.137.105.24', 16261), local_addr = None, remote_addr = None
    ff:ff:ff:ff:41:11:a6:88:7f
  Sending to ('23.137.105.24', 16261), local_addr = ('0.0.0.0', 0), remote_addr = None
    ff:ff:ff:ff:41:11:a6:88:7f
  Sending to ('23.137.105.24', 16261), local_addr = None, remote_addr = ('23.137.105.24', 16261)
    ff:ff:ff:ff:41:11:a6:88:7f
  Sending to ('23.137.105.24', 16261), local_addr = ('0.0.0.0', 0), remote_addr = ('23.137.105.24', 16261)
    ff:ff:ff:ff:41:11:a6:88:7f
  Sending to None, local_addr = None, remote_addr = ('23.137.105.24', 16261)
    ff:ff:ff:ff:41:9e:b1:3b:23
  Sending to None, local_addr = ('0.0.0.0', 0), remote_addr = ('23.137.105.24', 16261)
    ff:ff:ff:ff:41:9e:b1:3b:23
  Sending to ('play.thegamecracks.xyz', 16261), local_addr = None, remote_addr = None
    ff:ff:ff:ff:41:9e:b1:3b:23
  Sending to ('play.thegamecracks.xyz', 16261), local_addr = ('0.0.0.0', 0), remote_addr = None
    ff:ff:ff:ff:41:9e:b1:3b:23
  Sending to ('play.thegamecracks.xyz', 16261), local_addr = None, remote_addr = ('play.thegamecracks.xyz', 16261)
    Invalid address: must be None or ('23.137.105.24', 16261)
  Sending to ('play.thegamecracks.xyz', 16261), local_addr = ('0.0.0.0', 0), remote_addr = ('play.thegamecracks.xyz', 16261)
    can not get address information
  Sending to None, local_addr = None, remote_addr = ('play.thegamecracks.xyz', 16261)
    ff:ff:ff:ff:41:33:c9:63:d9
  Sending to None, local_addr = ('0.0.0.0', 0), remote_addr = ('play.thegamecracks.xyz', 16261)
    can not get address information

More simply organized into this big matrix:

Event Loop Address sendto(addr)? local_addr? remote_addr? Responded?
Proactor IPv4 ✔️
Proactor IPv4 ✔️ ✔️
Proactor IPv4 ✔️ ✔️
Proactor IPv4 ✔️ ✔️ ✔️
Proactor IPv4 ✔️
Proactor IPv4 ✔️ ✔️
Proactor Hostname ✔️
Proactor Hostname ✔️ ✔️
Proactor Hostname ✔️ ✔️ ⚠️
Proactor Hostname ✔️ ✔️ ✔️ ⚠️
Proactor Hostname ✔️
Proactor Hostname ✔️ ✔️ ⚠️
Selector IPv4 ✔️
Selector IPv4 ✔️ ✔️
Selector IPv4 ✔️ ✔️
Selector IPv4 ✔️ ✔️ ✔️
Selector IPv4 ✔️
Selector IPv4 ✔️ ✔️
Selector Hostname ✔️
Selector Hostname ✔️ ✔️
Selector Hostname ✔️ ✔️ ⚠️
Selector Hostname ✔️ ✔️ ✔️ ⚠️
Selector Hostname ✔️
Selector Hostname ✔️ ✔️ ⚠️

In the case of opengsq's UdpClient, it always sets sendto(addr) and local_addr, so these four rows apply:

Event Loop Address sendto(addr)? local_addr? remote_addr? Responded?
Proactor IPv4 ✔️ ✔️
Proactor Hostname ✔️ ✔️
Selector IPv4 ✔️ ✔️
Selector Hostname ✔️ ✔️

While this test used A2S instead of RakNet, this seems to match everything I described in my first message, where opengsq timed out with "mc.advancius.net" on Windows, but worked with "142.44.218.185". As for why it fails only with the proactor event loop, I guess it has something to do with that local_addr= note? But I don't know, I'm not a network doctor >.<

thegamecracks avatar Nov 07 '25 17:11 thegamecracks

It’s best practice to always resolve the hostname to an IP address before making any query with opengsq.

For example:

import socket

hostname = "play.thegamecracks.xyz"
ip = socket.gethostbyname(hostname)

print(f"Resolved {hostname} to {ip}")
# Use 'ip' instead of 'hostname' in your query call

Thanks for testing! Based on the testing results, queries using IPv4 always work perfectly and it clearly shows how inconsistent and messy the results can get when querying directly with the hostname 🫣🤣

BattlefieldDuck avatar Nov 08 '25 01:11 BattlefieldDuck

I Agree, that it should be still best practice of giving the IP Address instead of the hostname. While I know a lot of networking stuff my knowledge about the OS specific Python querks are limited.

Hornochs avatar Nov 11 '25 08:11 Hornochs