class Openra::Commands::Replays::ExtractData

Public Instance Methods

call(filepath) click to toggle source
# File lib/openra/commands/replays/extract_data.rb, line 9
def call(filepath)
  replay = Openra::Replays::Replay.new(filepath)
  support_powers = SUPPORT_POWERS.fetch(replay.metadata.mod, {})

  data = {
    mod: replay.metadata.mod,
    version: replay.metadata.version,
    server_name: nil,
    file: {
      hash: replay.file_hash
    },
    map: {
      name: utf8(replay.metadata.map_name),
      hash: replay.metadata.map_hash
    },
    game: {
      type: replay.players.each_with_object(Hash.new(0)) { |player, hash|
        if player.team.nil?
          hash[SecureRandom.uuid] += 1
        else
          hash[player.team] += 1
        end
      }.values.join('v'),
      start_time: replay.metadata.start_time.iso8601,
      end_time: replay.metadata.end_time.iso8601,
      duration: time((replay.metadata.end_time - replay.metadata.start_time) * 1000),
      options: nil
    },
    clients: [],
    chat: []
  }

  current_sync_clients = []
  current_sync_info = nil

  replay.each_order do |order|
    client = current_sync_clients.find do |candidate|
      candidate.index == order.client_index.to_s
    end

    case order.command
    when 'StartGame'
      data[:clients] = current_sync_info.clients.map do |client|
        player = replay.player(client.index)
        player_index = replay.players.index(player) + FIRST_PLAYER_INDEX if player

        {
          index: client.index,
          player_index: player_index,
          name: utf8(client.name),
          fingerprint: client.fingerprint,
          preferred_color: client.preferred_color,
          color: client.color,
          spawn: {
            random: player&.is_random_spawn,
            point: client.spawn_point
          },
          faction: {
            random: player&.is_random_faction,
            chosen: client.faction_name.downcase,
            actual: player&.faction_id
          },
          ip: client.ip,
          team: player&.team,
          is_bot: player&.is_bot || false,
          is_admin: client.is_admin,
          is_player: !player.nil?,
          is_winner: player&.outcome == 'Won',
          outcome_time: player&.outcome_time&.iso8601,
          build: [],
          support_powers: []
        }
      end

      data[:game][:options] = {
        explored_map: current_sync_info.global_settings.game_options.explored_map_enabled.value,
        speed: current_sync_info.global_settings.game_options.game_speed.value,
        starting_cash: current_sync_info.global_settings.game_options.starting_cash.value,
        starting_units: current_sync_info.global_settings.game_options.starting_units.value,
        fog_enabled: current_sync_info.global_settings.game_options.fog_enabled.value,
        cheats_enabled: current_sync_info.global_settings.game_options.cheats_enabled.value,
        kill_bounty_enabled: current_sync_info.global_settings.game_options.bounties_enabled.value,
        allow_undeploy: current_sync_info.global_settings.game_options.conyard_undeploy_enabled.value,
        crates_enabled: current_sync_info.global_settings.game_options.crates_enabled.value,
        build_off_allies: current_sync_info.global_settings.game_options.build_off_allies_enabled.value,
        restrict_build_radius: current_sync_info.global_settings.game_options.restricted_build_radius_enabled.value,
        short_game: current_sync_info.global_settings.game_options.short_game_enabled.value,
        techlevel: current_sync_info.global_settings.game_options.tech_level.value
      }
    when 'SyncInfo'
      current_sync_info = Openra::Struct::SyncInfo.new(
        Openra::MiniYAML.load(order.target)
      )
      current_sync_clients = current_sync_info.clients
    when 'SyncLobbyClients'
      next if order.target == "\n"

      current_sync_clients = Openra::Struct::SyncLobbyClients.new(
        Openra::MiniYAML.load(order.target)
      ).clients
    when *support_powers.keys
      key = support_powers.fetch(utf8(order.command))
      client_hash = data[:clients].find do |candidate|
        candidate[:index] == order.client_index.to_s
      end

      client_hash[:support_powers] << {
        type: key,
        game_time: time(order.frame.pred * current_sync_info.global_settings.frametime_multiplier),
        placement: cell(order.target_cell.to_i),
        extra_placement: cell(order.extra_cell.to_i),
      }
    when 'PlaceBuilding'
      # subject_id stores the player index here
      # as bot commands are issued by the host client
      client_hash = data[:clients].find do |candidate|
        candidate[:player_index] == order.subject_id
      end

      client_hash[:build] << {
        structure: utf8(order.target),
        game_time: time(order.frame.pred * current_sync_info.global_settings.frametime_multiplier),
        placement: cell(order.target_cell.to_i)
      }
    when 'Message'
      data[:chat] << {
        channel: 'server',
        name: nil,
        message: utf8(order.target)
      }
    when 'Chat'
      if order.extra_data.zero?
        data[:chat] << {
          channel: 'global',
          name: utf8(client.name),
          message: utf8(order.target)
        }
      else
        player = replay.player(client.index)

        if player
          data[:chat] << {
            channel: player.team,
            name: utf8(client.name),
            message: utf8(order.target)
          }
        else
          data[:chat] << {
            channel: 'spectators',
            name: utf8(client.name),
            message: utf8(order.target)
          }
        end
      end
    end
  end

  data[:server_name] = utf8(current_sync_info.global_settings.server_name)

  data
end