Class: Openra::Commands::Replays::ExtractData

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/openra/commands/replays/extract_data.rb

Instance Method Summary collapse

Methods included from Utils

#cell, #time, #utf8

Instance Method Details

#call(filepath) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# 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..mod, {})

  data = {
    mod: replay..mod,
    version: replay..version,
    server_name: nil,
    map: {
      name: utf8(replay..map_name),
      hash: replay..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..start_time.iso8601,
      end_time: replay..end_time.iso8601,
      duration: time((replay..end_time - replay..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'
      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'
      data[:chat] << {
        channel: 'global',
        name: utf8(client.name),
        message: utf8(order.target)
      }
    when 'TeamChat'
      data[:chat] << {
        channel: client.team,
        name: utf8(client.name),
        message: utf8(order.target)
      }
    end
  end

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

  data
end