Source code for mini_arcade_core.runtime.capture.replay
"""
Replay recording and playback functionality.
"""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Iterator, Optional
from mini_arcade_core.runtime.capture.replay_format import (
ReplayHeader,
ReplayReader,
ReplayWriter,
)
from mini_arcade_core.runtime.input_frame import InputFrame
[docs]
@dataclass
class ReplayRecorderConfig:
"""
Configuration for replay recording.
:ivar path (Path): Path to save the replay file.
:ivar header (ReplayHeader): Header information for the replay.
"""
path: Path
header: ReplayHeader
[docs]
class ReplayRecorder:
"""Recorder for game replays."""
def __init__(self):
self._writer: Optional[ReplayWriter] = None
@property
def active(self) -> bool:
"""
Check if the recorder is currently active.
:return: True if recording is active, False otherwise.
:rtype: bool
"""
return self._writer is not None
[docs]
def start(self, cfg: ReplayRecorderConfig) -> None:
"""
Start recording a replay.
:param cfg: Configuration for the replay recorder.
:type cfg: ReplayRecorderConfig
"""
if self._writer:
raise RuntimeError("ReplayRecorder already active")
self._writer = ReplayWriter(cfg.path, cfg.header)
self._writer.open()
[docs]
def record(self, frame: InputFrame) -> None:
"""
Record an input frame to the replay.
:param frame: The input frame to record.
:type frame: InputFrame
"""
if self._writer:
self._writer.write_frame(frame)
[docs]
def stop(self) -> None:
"""Stop recording the current replay."""
if self._writer:
self._writer.close()
self._writer = None
[docs]
class ReplayPlayer:
"""Player for game replays."""
def __init__(self):
self._reader: Optional[ReplayReader] = None
self._it: Optional[Iterator[InputFrame]] = None
self.header: Optional[ReplayHeader] = None
@property
def active(self) -> bool:
"""
Check if the player is currently active.
:return: True if a replay is being played, False otherwise.
:rtype: bool
"""
return self._it is not None
[docs]
def start(self, path: Path) -> ReplayHeader:
"""
Start playing back a replay.
:param path: Path to the replay file.
:type path: Path
:return: The header information of the replay.
:rtype: ReplayHeader
"""
if self._reader:
raise RuntimeError("ReplayPlayer already active")
self._reader = ReplayReader(path)
self.header = self._reader.open()
self._it = self._reader.frames()
return self.header
[docs]
def next(self) -> InputFrame:
"""
Get the next input frame from the replay.
:return: The next input frame.
:rtype: InputFrame
"""
if not self._it:
raise RuntimeError("ReplayPlayer not active")
try:
return next(self._it)
except StopIteration as exc:
self.stop()
raise RuntimeError("Replay finished") from exc
[docs]
def stop(self) -> None:
"""Stop playing back the current replay."""
if self._reader:
self._reader.close()
self._reader = None
self._it = None
self.header = None