Source code for bascenev1._replay

# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to replay playback."""

from __future__ import annotations  # Docs-generation hack.

import logging
import asyncio

import babase

import _bascenev1

from bascenev1._net import resolve_asset_packages_with_dialog

netlog = logging.getLogger('ba.net')

# The in-flight pre-playback task, if any (see new_replay_session's
# latest-wins behavior).
_g_preplay_task: asyncio.Task[None] | None = None


[docs] async def prepare_replay(file_name: str) -> bool: """Resolve a replay's required asset-packages before playback. The requirements are read from the replay file's header (no stream decompression) and anything not present locally is downloaded, with a cancelable progress dialog. Returns True when the replay is ready to play, or False if the user cancelled or a download failed. Safe to call with the launching UI still visible: on a False result the caller should leave that UI in place (a cancel then just closes the dialog and returns the user to where they were). (internal) """ # None => unreadable/incompatible file; let the launch surface it. # Empty => a pre-table replay with nothing to resolve. packages = _bascenev1.get_replay_asset_packages(file_name) if packages: netlog.debug( 'Replay %s requires %d asset-package(s); resolving.', file_name, len(packages), ) if not await resolve_asset_packages_with_dialog( packages, task=asyncio.current_task(), context=f'replay {file_name}' ): return False return True
[docs] def launch_replay(file_name: str) -> None: """Immediately start a session playing back a replay file. Assumes the replay's required asset-packages are already present locally; use :func:`new_replay_session` (or :func:`prepare_replay`) to resolve/download them first. This is the raw launch that the prep path hands off to. (internal) """ _bascenev1.new_replay_session(file_name)
[docs] def new_replay_session(file_name: str) -> None: """Prepare and play a replay: resolve content, then start playback. A one-call convenience combining :func:`prepare_replay` and :func:`launch_replay` -- content not present locally is downloaded (with a cancelable dialog) before playback begins, mirroring :func:`connect_to_party`'s pre-join content prep. A replay whose content is already local (the common case) or which predates asset-package tables starts immediately. (internal) """ assert babase.in_logic_thread() # Latest-wins: a new request cancels any pre-playback prep still in # flight. global _g_preplay_task # pylint: disable=global-statement if _g_preplay_task is not None and not _g_preplay_task.done(): _g_preplay_task.cancel() _g_preplay_task = None babase.app.create_async_task( _preplay_and_launch(file_name), name=f'new_replay_session {file_name}', )
async def _preplay_and_launch(file_name: str) -> None: """Content prep + the actual replay launch.""" global _g_preplay_task # pylint: disable=global-statement task = asyncio.current_task() _g_preplay_task = task try: if await prepare_replay(file_name): launch_replay(file_name) finally: if _g_preplay_task is task: _g_preplay_task = None # Docs-generation hack; import some stuff that we likely only forward-declared # in our actual source code so that docs tools can find it. from typing import (Coroutine, Any, Literal, Callable, Generator, Awaitable, Sequence, Self) import asyncio from concurrent.futures import Future from pathlib import Path from enum import Enum