class_docstr: Wrapper class for iterating over replay observation data.
  
  Yields the observations for a replay from player_id's perspective. The class
  can throw connection errors from the controller. These should be caught
  at the top level, since they are unrecoverable (the controller is unusable).
  
  Determines the replay version from the protocol information in the replay
  file. Uses a cache of processes, one for each binary version required
  to process corresponding replays.
  
  How to use the class:
  
  with ReplayObservationStream() as replay_stream:
    replay_stream.start_replay(replay_data, player_id)
  
    # Get game data if needed.
    info = replay_stream.game_info()
  
    for observation in replay_stream.observations():
      # Do something with each observation.
docstr: Yields a ResponseObservation proto for each environment step.
  
  If using the opponent's observations, this will yield a list of
  observations, one for each player.
  
  Args:
    step_sequence: A list of integers, the step sizes to apply to the stream.
docstr_tok: ["Yields","a","ResponseObservation","proto","for","each","environment","step",".","If","using","the","opponent","'s","observations",",","this","will","yield","a","list","of","observations",",","one","for","each","player",".","Args",":","step_sequence",":","A","list","of","integers",",","the","step","sizes","to","apply","to","the","stream","."]
code: def observations(self, step_sequence=None):
      """Yields a ResponseObservation proto for each environment step.
  
      If using the opponent's observations, this will yield a list of
      observations, one for each player.
  
      Args:
        step_sequence: A list of integers, the step sizes to apply to the stream.
      """
      self._packet_count = 0
      period_start = time.time()
      period = 1000  # log packet rate every 1000 packets
      logging.info("Begin iterating over frames...")
  
      while True:
          obs = [
              controller.observe() for controller in self._controllers
          ]
          if self._packet_count == 0:
              logging.info("The first packet has been read")
          self._packet_count += 1
          if len(obs) == 1:
              yield obs[0]
          else:
              yield obs
  
          if obs[0].player_result or (
              step_sequence and self._packet_count > len(step_sequence)
          ):
              # End of game.
              break
  
          if self._game_steps_per_episode > 0:
              if (
                  obs[0].observation.game_loop
                  >= self._game_steps_per_episode - 1
              ):
                  break
  
          for controller in self._controllers:
              if step_sequence and self._packet_count <= len(
                  step_sequence
              ):
                  step_mul = step_sequence[self._packet_count - 1]
              else:
                  step_mul = self._step_mul
  
              controller.step(step_mul)
  
          if self._packet_count % period == 0:
              time_taken = time.time() - period_start
              period_start = time.time()
              logging.info(
                  "Frame: %d, packets per sec: %.1f",
                  obs[0].observation.game_loop,
                  period / time_taken,
              )
  
imports: import io
  import json
  import time
  from absl import logging
  import mpyq
  from pysc2 import run_configs
  from s2clientprotocol import sc2api_pb2
label_desc: Use image node_id 8 for calling the ReplayObservationStream obj's underlying member method code with example usage: obj.observations(step_sequence) without return types