openra 2.3.1 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb765723f864f95b995b8fd38c8590c7ed916ed9dcbeaa16212eedf0ee32d1b5
4
- data.tar.gz: ad408fb02eb733dc1eaec9b9a7cdc1ab039f265f72d87f28ef6ff20e7e470738
3
+ metadata.gz: c9b7c45f11147948dc902fdca226050cec051fe3609dd5bd9c29a4d6bb8ce4a0
4
+ data.tar.gz: dbb255e98e93aa69dc8b0f151048ffbf6cdf95cb6e98dfed961620594ee012c0
5
5
  SHA512:
6
- metadata.gz: 4c53d05f0d0e9d5d5b6b424b1c96994f3c05f91cab405537c6ab9e0a2a81dd373a4bc9442520866af37ce95faa024d4727e4511797c9d7475674cef3a239077e
7
- data.tar.gz: 61ceaa4c5025b5fc5203084692f24b71c93207fedcd8efae883a05b038fd8cf966dd018ad94115c2020944286b5ed26b2d8cbd180cd7ab964350a916e3b51993
6
+ metadata.gz: 2e3289948dc5b00ceca957328329a25b1bab4cb0798926cd73c6cc17a813eb1de74f411b2da501c2f002b40597a45fe544902ad1851943aa7a16d489f63c24d2
7
+ data.tar.gz: 8219fca24372f10f2186b12574263786ba5f6cbc4f70771446cacbe8df0213c556af6f486d4f00ac61ab59824a363c67ed606631cbb60adc4f36cabf0c6c1eb3
@@ -1,6 +1,47 @@
1
1
  ## Unreleased
2
2
 
3
- [Compare v2.3.1...HEAD](https://github.com/AMHOL/openra-ruby/compare/v2.3.0...HEAD)
3
+ [Compare v4.0.2...HEAD](https://github.com/AMHOL/openra-ruby/compare/v4.0.2...HEAD)
4
+
5
+ ## v4.0.2
6
+
7
+ ### Fixed
8
+
9
+ * [maint] Fix chat spectator channel ([AMHOL](https://github.com/AMHOL))
10
+
11
+ [Compare v4.0.1...v4.0.2](https://github.com/AMHOL/openra-ruby/compare/v4.0.1...v4.0.2)
12
+
13
+ ## v4.0.1
14
+
15
+ ### Fixed
16
+
17
+ * [maint] Ignore empty SyncLobbyClients orders ([AMHOL](https://github.com/AMHOL))
18
+
19
+ [Compare v4.0.0...v4.0.1](https://github.com/AMHOL/openra-ruby/compare/v4.0.0...v4.0.1)
20
+
21
+ ## v4.0.0
22
+
23
+ ### Added
24
+
25
+ * [maint] Support playtest-20201213 - drops support for all older clients ([AMHOL](https://github.com/AMHOL))
26
+ * [core] Generate file hash in replay-data and replay-metadata commands ([AMHOL](https://github.com/AMHOL))
27
+
28
+ [Compare v3.0.1...v4.0.0](https://github.com/AMHOL/openra-ruby/compare/v3.0.1...v4.0.0)
29
+
30
+ ## v3.0.1
31
+
32
+ ### Added
33
+
34
+ * [maint] Added openra-commands file to load stand-alone commands ([AMHOL](https://github.com/AMHOL))
35
+
36
+ [Compare v3.0.0...v3.0.1](https://github.com/AMHOL/openra-ruby/compare/v3.0.0...v3.0.1)
37
+
38
+ ## v3.0.0
39
+
40
+ ### Added
41
+
42
+ * [replay-metadata] Renamed metadata command to replay-metadata ([AMHOL](https://github.com/AMHOL))
43
+
44
+ [Compare v2.3.1...v3.0.0](https://github.com/AMHOL/openra-ruby/compare/v2.3.0...v3.0.0)
4
45
 
5
46
  ## v2.3.1
6
47
 
data/README.md CHANGED
@@ -25,7 +25,7 @@ gem update openra
25
25
 
26
26
  ```sh
27
27
  openra replay-data /path/to/replay.orarep [--format json|pretty-json|yaml]
28
- openra metadata /path/to/replay.orarep [--format json|pretty-json|yaml]
28
+ openra replay-metadata /path/to/replay.orarep [--format json|pretty-json|yaml]
29
29
  ```
30
30
 
31
31
  ### Other tools
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openra/commands'
@@ -1,16 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'securerandom'
4
3
  require 'yaml'
5
4
  require 'json'
6
5
  require 'dry/cli'
7
6
  require 'openra/version'
8
- require 'openra/constants'
9
- require 'openra/replays'
10
- require 'openra/cli/utils'
7
+ require 'openra/commands'
11
8
  require 'openra/cli/formatters'
12
- require 'openra/cli/commands/metadata'
13
9
  require 'openra/cli/commands/replay_data'
10
+ require 'openra/cli/commands/replay_metadata'
14
11
  require 'openra/cli/commands/version'
15
12
  require 'openra/cli/command_registry'
16
13
 
@@ -5,8 +5,8 @@ module Openra
5
5
  class CommandRegistry
6
6
  extend Dry::CLI::Registry
7
7
 
8
- register 'metadata', Commands::Metadata
9
8
  register 'replay-data', Commands::ReplayData
9
+ register 'replay-metadata', Commands::ReplayMetadata
10
10
  register 'version', Commands::Version
11
11
  end
12
12
  end
@@ -4,155 +4,13 @@ module Openra
4
4
  class CLI
5
5
  module Commands
6
6
  class ReplayData < Dry::CLI::Command
7
- include CLI::Utils
8
-
9
7
  desc 'Output replay data to stdout'
10
8
 
11
9
  argument :replay, required: true, desc: 'Path of the replay file to read data from'
12
10
  option :format, default: 'json', values: %w(json pretty-json yaml), desc: 'Output format'
13
11
 
14
12
  def call(replay:, **options)
15
- replay = Openra::Replays::Replay.new(replay)
16
- support_powers = SUPPORT_POWERS.fetch(replay.metadata.mod, {})
17
-
18
- data = {
19
- mod: replay.metadata.mod,
20
- version: replay.metadata.version,
21
- server_name: nil,
22
- map: {
23
- name: utf8(replay.metadata.map_name),
24
- hash: replay.metadata.map_hash
25
- },
26
- game: {
27
- type: replay.players.each_with_object(Hash.new(0)) { |player, hash|
28
- if player.team.nil?
29
- hash[SecureRandom.uuid] += 1
30
- else
31
- hash[player.team] += 1
32
- end
33
- }.values.join('v'),
34
- start_time: replay.metadata.start_time.iso8601,
35
- end_time: replay.metadata.end_time.iso8601,
36
- duration: time((replay.metadata.end_time - replay.metadata.start_time) * 1000),
37
- options: nil
38
- },
39
- clients: [],
40
- chat: []
41
- }
42
-
43
- current_sync_clients = []
44
- current_sync_info = nil
45
-
46
- replay.each_order do |order|
47
- client = current_sync_clients.find do |candidate|
48
- candidate.index == order.client_index.to_s
49
- end
50
-
51
- case order.command
52
- when 'StartGame'
53
- data[:clients] = current_sync_info.clients.map do |client|
54
- player = replay.player(client.index)
55
- player_index = replay.players.index(player) + FIRST_PLAYER_INDEX if player
56
-
57
- {
58
- index: client.index,
59
- player_index: player_index,
60
- name: utf8(client.name),
61
- fingerprint: client.fingerprint,
62
- preferred_color: client.preferred_color,
63
- color: client.color,
64
- spawn: {
65
- random: player&.is_random_spawn,
66
- point: client.spawn_point
67
- },
68
- faction: {
69
- random: player&.is_random_faction,
70
- chosen: client.faction_name.downcase,
71
- actual: player&.faction_id
72
- },
73
- ip: client.ip,
74
- team: player&.team,
75
- is_bot: player&.is_bot || false,
76
- is_admin: client.is_admin,
77
- is_player: !player.nil?,
78
- is_winner: player&.outcome == 'Won',
79
- outcome_time: player&.outcome_time&.iso8601,
80
- build: [],
81
- support_powers: []
82
- }
83
- end
84
-
85
- data[:game][:options] = {
86
- explored_map: current_sync_info.global_settings.game_options.explored_map_enabled.value,
87
- speed: current_sync_info.global_settings.game_options.game_speed.value,
88
- starting_cash: current_sync_info.global_settings.game_options.starting_cash.value,
89
- starting_units: current_sync_info.global_settings.game_options.starting_units.value,
90
- fog_enabled: current_sync_info.global_settings.game_options.fog_enabled.value,
91
- cheats_enabled: current_sync_info.global_settings.game_options.cheats_enabled.value,
92
- kill_bounty_enabled: current_sync_info.global_settings.game_options.bounties_enabled.value,
93
- allow_undeploy: current_sync_info.global_settings.game_options.conyard_undeploy_enabled.value,
94
- crates_enabled: current_sync_info.global_settings.game_options.crates_enabled.value,
95
- build_off_allies: current_sync_info.global_settings.game_options.build_off_allies_enabled.value,
96
- restrict_build_radius: current_sync_info.global_settings.game_options.restricted_build_radius_enabled.value,
97
- short_game: current_sync_info.global_settings.game_options.short_game_enabled.value,
98
- techlevel: current_sync_info.global_settings.game_options.tech_level.value
99
- }
100
- when 'SyncInfo'
101
- current_sync_info = Openra::Struct::SyncInfo.new(
102
- Openra::MiniYAML.load(order.target)
103
- )
104
- current_sync_clients = current_sync_info.clients
105
- when 'SyncLobbyClients'
106
- current_sync_clients = Openra::Struct::SyncLobbyClients.new(
107
- Openra::MiniYAML.load(order.target)
108
- ).clients
109
- when *support_powers.keys
110
- key = support_powers.fetch(utf8(order.command))
111
- client_hash = data[:clients].find do |candidate|
112
- candidate[:index] == order.client_index.to_s
113
- end
114
-
115
- client_hash[:support_powers] << {
116
- type: key,
117
- game_time: time(order.frame.pred * current_sync_info.global_settings.frametime_multiplier),
118
- placement: cell(order.target_cell.to_i),
119
- extra_placement: cell(order.extra_cell.to_i),
120
- }
121
- when 'PlaceBuilding'
122
- # subject_id stores the player index here
123
- # as bot commands are issued by the host client
124
- client_hash = data[:clients].find do |candidate|
125
- candidate[:player_index] == order.subject_id
126
- end
127
-
128
- client_hash[:build] << {
129
- structure: utf8(order.target),
130
- game_time: time(order.frame.pred * current_sync_info.global_settings.frametime_multiplier),
131
- placement: cell(order.target_cell.to_i)
132
- }
133
- when 'Message'
134
- data[:chat] << {
135
- channel: 'server',
136
- name: nil,
137
- message: utf8(order.target)
138
- }
139
- when 'Chat'
140
- data[:chat] << {
141
- channel: 'global',
142
- name: utf8(client.name),
143
- message: utf8(order.target)
144
- }
145
- when 'TeamChat'
146
- data[:chat] << {
147
- channel: client.team,
148
- name: utf8(client.name),
149
- message: utf8(order.target)
150
- }
151
- end
152
- end
153
-
154
- data[:server_name] = utf8(current_sync_info.global_settings.server_name)
155
-
13
+ data = Openra::Commands::Replays::ExtractData.new.call(replay)
156
14
  puts FORMATTERS.fetch(options[:format]).call(data)
157
15
  end
158
16
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openra
4
+ class CLI
5
+ module Commands
6
+ class ReplayMetadata < Dry::CLI::Command
7
+ desc 'Output replay metadata to stdout'
8
+
9
+ argument :replay, required: true, desc: 'Path of the replay file to read data from'
10
+ option :format, default: 'json', values: %w(json pretty-json yaml), desc: 'Output format'
11
+
12
+ def call(replay:, **options)
13
+ data = Openra::Commands::Replays::ExtractMetadata.new.call(replay)
14
+ puts FORMATTERS.fetch(options[:format]).call(data)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ require 'securerandom'
2
+ require 'openra/constants'
3
+ require 'openra/replays'
4
+ require 'openra/commands/utils'
5
+ require 'openra/commands/replays/extract_metadata'
6
+ require 'openra/commands/replays/extract_data'
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openra
4
+ module Commands
5
+ module Replays
6
+ class ExtractData
7
+ include Utils
8
+
9
+ def call(filepath)
10
+ replay = Openra::Replays::Replay.new(filepath)
11
+ support_powers = SUPPORT_POWERS.fetch(replay.metadata.mod, {})
12
+
13
+ data = {
14
+ mod: replay.metadata.mod,
15
+ version: replay.metadata.version,
16
+ server_name: nil,
17
+ file: {
18
+ hash: replay.file_hash
19
+ },
20
+ map: {
21
+ name: utf8(replay.metadata.map_name),
22
+ hash: replay.metadata.map_hash
23
+ },
24
+ game: {
25
+ type: replay.players.each_with_object(Hash.new(0)) { |player, hash|
26
+ if player.team.nil?
27
+ hash[SecureRandom.uuid] += 1
28
+ else
29
+ hash[player.team] += 1
30
+ end
31
+ }.values.join('v'),
32
+ start_time: replay.metadata.start_time.iso8601,
33
+ end_time: replay.metadata.end_time.iso8601,
34
+ duration: time((replay.metadata.end_time - replay.metadata.start_time) * 1000),
35
+ options: nil
36
+ },
37
+ clients: [],
38
+ chat: []
39
+ }
40
+
41
+ current_sync_clients = []
42
+ current_sync_info = nil
43
+
44
+ replay.each_order do |order|
45
+ client = current_sync_clients.find do |candidate|
46
+ candidate.index == order.client_index.to_s
47
+ end
48
+
49
+ case order.command
50
+ when 'StartGame'
51
+ data[:clients] = current_sync_info.clients.map do |client|
52
+ player = replay.player(client.index)
53
+ player_index = replay.players.index(player) + FIRST_PLAYER_INDEX if player
54
+
55
+ {
56
+ index: client.index,
57
+ player_index: player_index,
58
+ name: utf8(client.name),
59
+ fingerprint: client.fingerprint,
60
+ preferred_color: client.preferred_color,
61
+ color: client.color,
62
+ spawn: {
63
+ random: player&.is_random_spawn,
64
+ point: client.spawn_point
65
+ },
66
+ faction: {
67
+ random: player&.is_random_faction,
68
+ chosen: client.faction_name.downcase,
69
+ actual: player&.faction_id
70
+ },
71
+ ip: client.ip,
72
+ team: player&.team,
73
+ is_bot: player&.is_bot || false,
74
+ is_admin: client.is_admin,
75
+ is_player: !player.nil?,
76
+ is_winner: player&.outcome == 'Won',
77
+ outcome_time: player&.outcome_time&.iso8601,
78
+ build: [],
79
+ support_powers: []
80
+ }
81
+ end
82
+
83
+ data[:game][:options] = {
84
+ explored_map: current_sync_info.global_settings.game_options.explored_map_enabled.value,
85
+ speed: current_sync_info.global_settings.game_options.game_speed.value,
86
+ starting_cash: current_sync_info.global_settings.game_options.starting_cash.value,
87
+ starting_units: current_sync_info.global_settings.game_options.starting_units.value,
88
+ fog_enabled: current_sync_info.global_settings.game_options.fog_enabled.value,
89
+ cheats_enabled: current_sync_info.global_settings.game_options.cheats_enabled.value,
90
+ kill_bounty_enabled: current_sync_info.global_settings.game_options.bounties_enabled.value,
91
+ allow_undeploy: current_sync_info.global_settings.game_options.conyard_undeploy_enabled.value,
92
+ crates_enabled: current_sync_info.global_settings.game_options.crates_enabled.value,
93
+ build_off_allies: current_sync_info.global_settings.game_options.build_off_allies_enabled.value,
94
+ restrict_build_radius: current_sync_info.global_settings.game_options.restricted_build_radius_enabled.value,
95
+ short_game: current_sync_info.global_settings.game_options.short_game_enabled.value,
96
+ techlevel: current_sync_info.global_settings.game_options.tech_level.value
97
+ }
98
+ when 'SyncInfo'
99
+ current_sync_info = Openra::Struct::SyncInfo.new(
100
+ Openra::MiniYAML.load(order.target)
101
+ )
102
+ current_sync_clients = current_sync_info.clients
103
+ when 'SyncLobbyClients'
104
+ next if order.target == "\n"
105
+
106
+ current_sync_clients = Openra::Struct::SyncLobbyClients.new(
107
+ Openra::MiniYAML.load(order.target)
108
+ ).clients
109
+ when *support_powers.keys
110
+ key = support_powers.fetch(utf8(order.command))
111
+ client_hash = data[:clients].find do |candidate|
112
+ candidate[:index] == order.client_index.to_s
113
+ end
114
+
115
+ client_hash[:support_powers] << {
116
+ type: key,
117
+ game_time: time(order.frame.pred * current_sync_info.global_settings.frametime_multiplier),
118
+ placement: cell(order.target_cell.to_i),
119
+ extra_placement: cell(order.extra_cell.to_i),
120
+ }
121
+ when 'PlaceBuilding'
122
+ # subject_id stores the player index here
123
+ # as bot commands are issued by the host client
124
+ client_hash = data[:clients].find do |candidate|
125
+ candidate[:player_index] == order.subject_id
126
+ end
127
+
128
+ client_hash[:build] << {
129
+ structure: utf8(order.target),
130
+ game_time: time(order.frame.pred * current_sync_info.global_settings.frametime_multiplier),
131
+ placement: cell(order.target_cell.to_i)
132
+ }
133
+ when 'Message'
134
+ data[:chat] << {
135
+ channel: 'server',
136
+ name: nil,
137
+ message: utf8(order.target)
138
+ }
139
+ when 'Chat'
140
+ if order.extra_data.zero?
141
+ data[:chat] << {
142
+ channel: 'global',
143
+ name: utf8(client.name),
144
+ message: utf8(order.target)
145
+ }
146
+ else
147
+ player = replay.player(client.index)
148
+
149
+ if player
150
+ data[:chat] << {
151
+ channel: player.team,
152
+ name: utf8(client.name),
153
+ message: utf8(order.target)
154
+ }
155
+ else
156
+ data[:chat] << {
157
+ channel: 'spectators',
158
+ name: utf8(client.name),
159
+ message: utf8(order.target)
160
+ }
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ data[:server_name] = utf8(current_sync_info.global_settings.server_name)
167
+
168
+ data
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -1,22 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Openra
4
- class CLI
5
- module Commands
6
- class Metadata < Dry::CLI::Command
7
- include CLI::Utils
4
+ module Commands
5
+ module Replays
6
+ class ExtractMetadata
7
+ include Utils
8
8
 
9
- desc 'Output replay metadata to stdout'
9
+ def call(filepath)
10
+ replay = Openra::Replays::Replay.new(filepath)
10
11
 
11
- argument :replay, required: true, desc: 'Path of the replay file to read data from'
12
- option :format, default: 'json', values: %w(json pretty-json yaml), desc: 'Output format'
13
-
14
- def call(replay:, **options)
15
- replay = Openra::Replays::Replay.new(replay)
16
-
17
- data = {
12
+ {
18
13
  mod: replay.metadata.mod,
19
14
  version: replay.metadata.version,
15
+ file: {
16
+ hash: replay.file_hash
17
+ },
20
18
  map: {
21
19
  name: utf8(replay.metadata.map_name),
22
20
  hash: replay.metadata.map_hash
@@ -55,8 +53,6 @@ module Openra
55
53
  }
56
54
  }
57
55
  }
58
-
59
- puts FORMATTERS.fetch(options[:format]).call(data)
60
56
  end
61
57
  end
62
58
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Openra
4
- class CLI
4
+ module Commands
5
5
  module Utils
6
6
  def utf8(string)
7
7
  string.force_encoding('UTF-8')
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'date'
4
+ require 'digest'
4
5
  require 'bindata'
5
6
  require 'bindata/big_integer'
6
7
  require 'bindata/pascal_string'
@@ -24,6 +24,10 @@ module Openra
24
24
  end
25
25
  end
26
26
 
27
+ def generate_hash
28
+ Digest::SHA256.hexdigest(file.read)
29
+ end
30
+
27
31
  def metadata
28
32
  # https://github.com/OpenRA/OpenRA/blob/23b3c237b7071fd308c4664b0b6c5d719c0f3c74/OpenRA.Game/FileFormats/ReplayMetadata.cs#L96
29
33
  @metadata ||= begin
@@ -6,19 +6,22 @@ module Openra
6
6
  class Order < BinData::Record
7
7
  HEX_FE = ?\xFE.dup.force_encoding('ASCII-8BIT').freeze
8
8
  HEX_FF = ?\xFF.dup.force_encoding('ASCII-8BIT').freeze
9
- # NEED TO LEARN HOW TO READ FLAGS AND TARGET TYPE
9
+
10
10
  IS_STANDARD_ORDER = -> { order_type == HEX_FF }
11
11
  IS_IMMEDIATE_ORDER = -> { order_type == HEX_FE }
12
- HAS_TARGET = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 1) == 1 }
12
+ HAS_TARGET = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 0x01) == 0x01 }
13
+ HAS_EXTRA_ACTORS = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 0x02) == 0x02 }
13
14
  TARGET_IS_ACTOR = -> { instance_exec(&HAS_TARGET) && target_type == 1 }
14
15
  TARGET_IS_TERRAIN = -> { instance_exec(&HAS_TARGET) && target_type == 2 }
15
16
  TARGET_IS_FROZEN_ACTOR = -> { instance_exec(&HAS_TARGET) && target_type == 3 }
16
- TARGET_IS_CELL = -> { instance_exec(&TARGET_IS_TERRAIN) && (flags & 64) == 64 }
17
- TARGET_NOT_CELL = -> { instance_exec(&TARGET_IS_TERRAIN) && (flags & 64) != 64 }
18
- HAS_SUBJECT = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 128) == 128 }
19
- HAS_TARGET_STRING = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 4) == 4 }
20
- HAS_EXTRA_LOCATION = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 16) == 16 }
21
- HAS_EXTRA_DATA = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 32) == 32 }
17
+ TARGET_IS_CELL = -> { instance_exec(&TARGET_IS_TERRAIN) && (flags & 0x40) == 0x40 }
18
+ TARGET_NOT_CELL = -> { instance_exec(&TARGET_IS_TERRAIN) && (flags & 0x40) != 0x40 }
19
+ HAS_SUBJECT = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 0x80) == 0x80 }
20
+ HAS_TARGET_STRING = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 0x04) == 0x04 }
21
+ HAS_EXTRA_LOCATION = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 0x10) == 0x10 }
22
+ HAS_EXTRA_DATA = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 0x20) == 0x20 }
23
+ # IS_QUEUED = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 0x08) == 0x08 }
24
+ IS_GROUPED = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 0x100) == 0x100 }
22
25
 
23
26
  endian :little
24
27
  # Common
@@ -27,7 +30,7 @@ module Openra
27
30
  # Immediate Order Data
28
31
  pascal_string :immediate_order_target, onlyif: IS_IMMEDIATE_ORDER
29
32
  # Standard Order Data
30
- uint8 :flags, onlyif: IS_STANDARD_ORDER
33
+ int16 :flags, onlyif: IS_STANDARD_ORDER
31
34
  uint32 :subject_id, onlyif: HAS_SUBJECT
32
35
  uint8 :target_type, onlyif: HAS_TARGET
33
36
  uint32 :actor_id, onlyif: TARGET_IS_ACTOR
@@ -39,8 +42,12 @@ module Openra
39
42
  int32 :pos_y, onlyif: TARGET_NOT_CELL
40
43
  int32 :pos_z, onlyif: TARGET_NOT_CELL
41
44
  pascal_string :standard_order_target, onlyif: HAS_TARGET_STRING
45
+ int32 :extra_actors_count, onlyif: HAS_EXTRA_ACTORS
46
+ array :extra_actor_ids, type: :uint32, initial_length: :extra_actors_count, onlyif: HAS_EXTRA_ACTORS
42
47
  int32 :extra_cell, onlyif: HAS_EXTRA_LOCATION
43
48
  uint32 :extra_data, onlyif: HAS_EXTRA_DATA
49
+ int32 :grouped_actors_count, onlyif: IS_GROUPED
50
+ array :grouped_actor_ids, type: :uint32, initial_length: :grouped_actors_count, onlyif: IS_GROUPED
44
51
 
45
52
  def target
46
53
  standard? ? standard_order_target : immediate_order_target
@@ -9,6 +9,10 @@ module Openra
9
9
  @file = Openra::Replays::File.new(filename)
10
10
  end
11
11
 
12
+ def file_hash
13
+ file.generate_hash
14
+ end
15
+
12
16
  def metadata
13
17
  @metadata ||= Openra::Struct::Metadata.new(
14
18
  Openra::MiniYAML.load(file.metadata.data)
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'dry/transformer/all'
4
- require 'dry-struct'
5
4
  require 'openra/types'
5
+ require 'dry-struct'
6
6
  require 'openra/struct/functions'
7
7
  require 'openra/struct/pre_processor'
8
8
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Openra
4
- VERSION = '2.3.1'.freeze
4
+ VERSION = '4.0.2'.freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openra
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 4.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Holland
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-11 00:00:00.000000000 Z
11
+ date: 2021-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -136,7 +136,7 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
- description:
139
+ description:
140
140
  email:
141
141
  - andyholland1991@aol.com
142
142
  executables:
@@ -151,6 +151,7 @@ files:
151
151
  - lib/bindata/big_integer.rb
152
152
  - lib/bindata/pascal_string.rb
153
153
  - lib/openra-cli.rb
154
+ - lib/openra-commands.rb
154
155
  - lib/openra-mini_yaml.rb
155
156
  - lib/openra-replays.rb
156
157
  - lib/openra-struct.rb
@@ -158,11 +159,14 @@ files:
158
159
  - lib/openra.rb
159
160
  - lib/openra/cli.rb
160
161
  - lib/openra/cli/command_registry.rb
161
- - lib/openra/cli/commands/metadata.rb
162
162
  - lib/openra/cli/commands/replay_data.rb
163
+ - lib/openra/cli/commands/replay_metadata.rb
163
164
  - lib/openra/cli/commands/version.rb
164
165
  - lib/openra/cli/formatters.rb
165
- - lib/openra/cli/utils.rb
166
+ - lib/openra/commands.rb
167
+ - lib/openra/commands/replays/extract_data.rb
168
+ - lib/openra/commands/replays/extract_metadata.rb
169
+ - lib/openra/commands/utils.rb
166
170
  - lib/openra/constants.rb
167
171
  - lib/openra/mini_yaml.rb
168
172
  - lib/openra/replays.rb
@@ -194,7 +198,7 @@ homepage: https://github.com/AMHOL/openra-ruby
194
198
  licenses:
195
199
  - MIT
196
200
  metadata: {}
197
- post_install_message:
201
+ post_install_message:
198
202
  rdoc_options: []
199
203
  require_paths:
200
204
  - lib
@@ -209,8 +213,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
213
  - !ruby/object:Gem::Version
210
214
  version: '0'
211
215
  requirements: []
212
- rubygems_version: 3.1.2
213
- signing_key:
216
+ rubygems_version: 3.2.3
217
+ signing_key:
214
218
  specification_version: 4
215
219
  summary: Openra Rubygem
216
220
  test_files: []