mirror of
https://github.com/hypervortex/VH-Bombsquad-Modded-Server-Files
synced 2025-10-16 12:02:51 +00:00
92 lines
3 KiB
Python
92 lines
3 KiB
Python
# Released under the MIT License. See LICENSE for details.
|
|
#
|
|
"""Asyncio related functionality.
|
|
|
|
Exploring the idea of allowing Python coroutines to run gracefully
|
|
besides our internal event loop. They could prove useful for networking
|
|
operations or possibly game logic.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
import asyncio
|
|
import logging
|
|
import time
|
|
import os
|
|
|
|
if TYPE_CHECKING:
|
|
import ba
|
|
|
|
# Our timer and event loop for the ballistica logic thread.
|
|
_asyncio_timer: ba.Timer | None = None
|
|
_asyncio_event_loop: asyncio.AbstractEventLoop | None = None
|
|
|
|
DEBUG_TIMING = os.environ.get('BA_DEBUG_TIMING') == '1'
|
|
|
|
|
|
def setup_asyncio() -> asyncio.AbstractEventLoop:
|
|
"""Setup asyncio functionality for the logic thread."""
|
|
# pylint: disable=global-statement
|
|
|
|
import _ba
|
|
import ba
|
|
from ba._generated.enums import TimeType
|
|
|
|
assert _ba.in_logic_thread()
|
|
|
|
# Create our event-loop. We don't expect there to be one
|
|
# running on this thread before we do.
|
|
try:
|
|
asyncio.get_running_loop()
|
|
print('Found running asyncio loop; unexpected.')
|
|
except RuntimeError:
|
|
pass
|
|
|
|
global _asyncio_event_loop # pylint: disable=invalid-name
|
|
_asyncio_event_loop = asyncio.new_event_loop()
|
|
_asyncio_event_loop.set_default_executor(ba.app.threadpool)
|
|
|
|
# Ideally we should integrate asyncio into our C++ Thread class's
|
|
# low level event loop so that asyncio timers/sockets/etc. could
|
|
# be true first-class citizens. For now, though, we can explicitly
|
|
# pump an asyncio loop periodically which gets us a decent
|
|
# approximation of that, which should be good enough for
|
|
# all but extremely time sensitive uses.
|
|
# See https://stackoverflow.com/questions/29782377/
|
|
# is-it-possible-to-run-only-a-single-step-of-the-asyncio-event-loop
|
|
def run_cycle() -> None:
|
|
assert _asyncio_event_loop is not None
|
|
_asyncio_event_loop.call_soon(_asyncio_event_loop.stop)
|
|
starttime = time.monotonic() if DEBUG_TIMING else 0
|
|
_asyncio_event_loop.run_forever()
|
|
endtime = time.monotonic() if DEBUG_TIMING else 0
|
|
|
|
# Let's aim to have nothing take longer than 1/120 of a second.
|
|
if DEBUG_TIMING:
|
|
warn_time = 1.0 / 120
|
|
duration = endtime - starttime
|
|
if duration > warn_time:
|
|
logging.warning(
|
|
'Asyncio loop step took %.4fs; ideal max is %.4f',
|
|
duration,
|
|
warn_time,
|
|
)
|
|
|
|
global _asyncio_timer # pylint: disable=invalid-name
|
|
_asyncio_timer = _ba.Timer(
|
|
1.0 / 30.0, run_cycle, timetype=TimeType.REAL, repeat=True
|
|
)
|
|
|
|
if bool(False):
|
|
|
|
async def aio_test() -> None:
|
|
print('TEST AIO TASK STARTING')
|
|
assert _asyncio_event_loop is not None
|
|
assert asyncio.get_running_loop() is _asyncio_event_loop
|
|
await asyncio.sleep(2.0)
|
|
print('TEST AIO TASK ENDING')
|
|
|
|
_asyncio_event_loop.create_task(aio_test())
|
|
|
|
return _asyncio_event_loop
|