Module: Sc2::Connection::Requests

Included in:
Sc2::Connection
Defined in:
lib/sc2ai/connection/requests.rb

Overview

Sends protobuf requests over Connection to Client

Instance Method Summary collapse

Instance Method Details

#action(actions) ⇒ Api::ResponseAction

Executes an array of [Api::Action] for a participant

Parameters:

Returns:



247
248
249
250
251
# File 'lib/sc2ai/connection/requests.rb', line 247

def action(actions)
  send_request_for action: Api::RequestAction.new(
    actions: actions
  )
end

#available_mapsApi::ResponseAvailableMaps

Returns directory of maps that can be played on.

Returns:



394
395
396
# File 'lib/sc2ai/connection/requests.rb', line 394

def available_maps
  send_request_for available_maps: Api::RequestAvailableMaps.new
end

#create_game(map:, players:, realtime: false) ⇒ Object

Send to host to initialize game



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/sc2ai/connection/requests.rb', line 10

def create_game(map:, players:, realtime: false)
  send_request_for create_game: Api::RequestCreateGame.new(
    local_map: Api::LocalMap.new(map_path: map.path),
    player_setup: players.map do |player|
      Api::PlayerSetup.new(
        type: player.type,
        race: player.race,
        player_name: player.name,
        difficulty: player.difficulty,
        ai_build: player.ai_build
      )
    end,
    realtime:
  )
end

#data(ability_id: true, unit_type_id: true, upgrade_id: true, buff_id: true, effect_id: true) ⇒ Api::ResponseData

Data about different gameplay elements. May be different for different games. Note that buff_id and effect_id gives worse quality data than generated from stableids (EffectId and BuffId) Those options are disabled by default

Parameters:

  • ability_id (Boolean) (defaults to: true)

    to include ability data

  • unit_type_id (Boolean) (defaults to: true)

    to include unit data

  • upgrade_id (Boolean) (defaults to: true)

    to include upgrade data

  • buff_id (Boolean) (defaults to: true)

    to get include buff data

  • effect_id (Boolean) (defaults to: true)

    to get to include effect data

Returns:



186
187
188
189
190
191
192
193
194
# File 'lib/sc2ai/connection/requests.rb', line 186

def data(ability_id: true, unit_type_id: true, upgrade_id: true, buff_id: true, effect_id: true)
  send_request_for data: Api::RequestData.new(
    ability_id:,
    unit_type_id:,
    upgrade_id:,
    buff_id:,
    effect_id:
  )
end

#debug(commands) ⇒ void

This method returns an undefined value.

Display debug information and execute debug actions

Parameters:



411
412
413
414
415
# File 'lib/sc2ai/connection/requests.rb', line 411

def debug(commands)
  send_request_for debug: Api::RequestDebug.new(
    debug: commands
  )
end

#game_infoApi::ResponseGameInfo

Static data about the current game and map.



173
174
175
# File 'lib/sc2ai/connection/requests.rb', line 173

def game_info
  send_request_for game_info: Api::RequestGameInfo.new
end

#join_game(race:, name:, server_host:, port_config:, enable_feature_layer: false, interface_options: {}) ⇒ Object

Send to host and all clients for game to begin.

Parameters:

  • race (Integer)

    see Api::Race

  • name (String)

    player name

  • server_host (String)

    hostname or ip of sc2 client

  • port_config (Sc2::PortConfig)

    port config auto or basic, using start port

  • enable_feature_layer (Boolean) (defaults to: false)

    Enables the feature layer at 1x1 pixels

  • interface_options (Hash) (defaults to: {})

Options Hash (interface_options:):

  • :raw (Boolean) — default: true

    raw interface enabled, default true

  • :score (Boolean) — default: false

    score game info

  • :show_cloaked (Boolean) — default: true

    hows details about cloaked units

  • :show_burrowed_shadows (Boolean) — default: true

    shows some details for those that produce a shadow

  • :show_placeholders (Boolean) — default: true

    return placeholder units (buildings to be constructed)

  • :raw_affects_selection (Boolean) — default: false

    for live raw does whatever it wants, for local it keeps your selection by default.

  • :raw_crop_to_playable_area (Boolean) — default: true

    trims away unplayable parts of map, else map is 255x255 with dead space. performant if true.



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
# File 'lib/sc2ai/connection/requests.rb', line 40

def join_game(race:, name:, server_host:, port_config:, enable_feature_layer: false, interface_options: {})
  interface_options ||= {}

  send_request_for join_game: Api::RequestJoinGame.new(
    # TODO: For Observer support, get player_index for observer,
    #   don't set race and pass observed_player_id: player_index
    # observed_player_id: 0, # For observer
    # --
    race:,
    player_name: name,
    host_ip: server_host,
    server_ports: port_config.server_port_set,
    client_ports: port_config.client_port_sets,
    options: Api::InterfaceOptions.new(
      raw: true,
      score: false,
      feature_layer: feature_layer_interface_options(enable_feature_layer),
      show_cloaked: true,
      show_burrowed_shadows: true,
      show_placeholders: true,
      raw_affects_selection: Sc2.ladder?,
      raw_crop_to_playable_area: true, **interface_options
    )
  )
end

#leave_gameObject

Multiplayer only. Disconnects from a multiplayer game, equivalent to surrender. Keeps client alive.



148
149
150
# File 'lib/sc2ai/connection/requests.rb', line 148

def leave_game
  send_request_for leave_game: Api::RequestLeaveGame.new
end

#observation(game_loop: nil) ⇒ Object

Snapshot of the current game state. Primary source for raw information

Parameters:

  • game_loop (Integer) (defaults to: nil)

    you wish to wait for (realtime only)



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/sc2ai/connection/requests.rb', line 198

def observation(game_loop: nil)
  # Sc2.logger.debug { "#{self.class}.#{__method__} game_loop: #{game_loop}" }
  if game_loop.nil?
    # Uncomment to enable multiple gc
    # Async do
    #   result = Async do

    @_cached_request_observation ||= Api::Request.new(
      observation: Api::RequestObservation.new
    ).to_proto
    @websocket.send_binary(@_cached_request_observation)
    response = Api::Response.decode(@websocket.read.to_str)

    if @status != response.status
      @status = response.status
      @listeners[StatusListener.name]&.each { _1.on_status_change(@status) }
    end

    response.observation

    # Uncomment to enable manual GC
    # end

    #   Async do
    #     # A step command is synchronous for both bots.
    #     # Bot A will wait for Bot B, then both get responses.
    #     # If we're ahead or even not, we can perform a minor GC sweep while we wait.
    #     # If the server notifies the other machine first
    #     # This smooths out unexpected hiccups and reduces overall major gc sweeps, possibly for free.
    #     begin
    #       GC.start(full_mark: false, immediate_sweep: true)
    #       # if rand(100).zero? # Just below every 5 seconds
    #       #   GC.compact
    #       # end
    #     rescue
    #       # noop - just here for cleaner exceptions on interrupt
    #     end
    #   end
    #   result.wait
    # end.wait

  else
    send_request_for observation: Api::RequestObservation.new(game_loop:)
  end
end

#observer_action(actions) ⇒ Object

Executes an actions for an observer.

Parameters:



255
256
257
258
259
260
261
# File 'lib/sc2ai/connection/requests.rb', line 255

def observer_action(actions)
  # ActionObserverCameraMove camera_move = 2;
  # ActionObserverCameraFollowPlayer camera_follow_player = 3;
  send_request_for obs_action: Api::RequestObserverAction.new(
    actions: actions
  )
end

#observer_action_camera_move(world_pos, distance = 0) ⇒ Object

Moves observer camera to a position at a distance

Parameters:

  • world_pos (Api::Point2D)
  • distance (Float) (defaults to: 0)

    Distance between camera and terrain. Larger value zooms out camera. Defaults to standard camera distance if set to 0.



266
267
268
269
270
271
272
273
# File 'lib/sc2ai/connection/requests.rb', line 266

def observer_action_camera_move(world_pos, distance = 0)
  observer_action([Api::ObserverAction.new(
    camera_move: Api::ActionObserverCameraMove.new(
      world_pos:,
      distance:
    )
  )])
end

#pingObject

Network ping for testing connection.



404
405
406
# File 'lib/sc2ai/connection/requests.rb', line 404

def ping
  send_request_for ping: Api::RequestPing.new
end

#query(pathing: nil, abilities: nil, placements: nil, ignore_resource_requirements: false) ⇒ Api::ResponseQuery

Additional methods for inspecting game state. Synchronous and must wait on response

Parameters:

Returns:



291
292
293
294
295
296
297
298
# File 'lib/sc2ai/connection/requests.rb', line 291

def query(pathing: nil, abilities: nil, placements: nil, ignore_resource_requirements: false)
  send_request_for query: Api::RequestQuery.new(
    pathing:,
    abilities:,
    placements:,
    ignore_resource_requirements:
  )
end

#query_abilities(queries, ignore_resource_requirements: false) ⇒ Array<Api::ResponseQueryAvailableAbilities>

Queries one or more ability-available checks

Parameters:

  • queries (Array<Api::RequestQueryAvailableAbilities>)

    one or more pathing queries

  • ignore_resource_requirements (Boolean) (defaults to: false)

    Ignores requirements like food, minerals and so on.

Returns:



316
317
318
319
320
321
322
323
324
# File 'lib/sc2ai/connection/requests.rb', line 316

def query_abilities(queries, ignore_resource_requirements: false)
  arr_queries = queries.is_a?(Array) ? queries : [queries]

  response = send_request_for query: Api::RequestQuery.new(
    abilities: arr_queries,
    ignore_resource_requirements:
  )
  response.abilities
end

#query_abilities_for_unit_tags(unit_tags, ignore_resource_requirements: false) ⇒ Array<Api::ResponseQueryAvailableAbilities>

Queries available abilities for units

Parameters:

  • unit_tags (Array<Integer>)

    an array of unit tags or a single tag

  • ignore_resource_requirements (Boolean) (defaults to: false)

    Ignores requirements like food, minerals and so on.

Returns:



330
331
332
333
334
335
336
337
338
339
# File 'lib/sc2ai/connection/requests.rb', line 330

def query_abilities_for_unit_tags(unit_tags, ignore_resource_requirements: false)
  return [] if unit_tags.nil?
  queries = []
  unit_tags = [unit_tags] unless unit_tags.is_a? Array
  unit_tags.each do |unit_tag|
    queries << Api::RequestQueryAvailableAbilities.new(unit_tag: unit_tag)
  end

  query_abilities(queries, ignore_resource_requirements:)
end

#query_ability_ids_for_unit(unit, ignore_resource_requirements: false) ⇒ Array<Integer>

Queries available ability ids for one unit Shortened response over #query_abilities_for_unit_tags, since we know the tag already and can just return an array of ability ids. Note: Querying single units are expensive and should be batched with #query_abilities_for_unit_tags

Parameters:

  • unit (Api::Unit, Integer)

    a unit or a tag.

Returns:

  • (Array<Integer>)

    array of ability ids



347
348
349
350
351
352
353
354
355
# File 'lib/sc2ai/connection/requests.rb', line 347

def query_ability_ids_for_unit(unit, ignore_resource_requirements: false)
  tag = unit.is_a?(Api::Unit) ? unit.tag : unit
  result = query_abilities_for_unit_tags([tag], ignore_resource_requirements:)
  if result.nil?
    []
  else
    result.first.abilities.map(&:ability_id)
  end
end

#query_pathings(queries) ⇒ Array<Api::ResponseQueryPathing>

Queries one or more pathing queries

Parameters:

Returns:



303
304
305
306
307
308
309
310
# File 'lib/sc2ai/connection/requests.rb', line 303

def query_pathings(queries)
  arr_queries = queries.is_a?(Array) ? queries : [queries]

  response = send_request_for query: Api::RequestQuery.new(
    pathing: arr_queries
  )
  response.pathing
end

#query_placements(queries) ⇒ Array<Api::ResponseQueryBuildingPlacement>

Queries one or more pathing queries

Parameters:

Returns:



360
361
362
363
364
365
366
# File 'lib/sc2ai/connection/requests.rb', line 360

def query_placements(queries)
  arr_queries = queries.is_a?(Array) ? queries : [queries]

  response = query(placements: arr_queries)

  response.placements
end

#quitObject

Quits Sc2. Does not work on ladder.



163
164
165
# File 'lib/sc2ai/connection/requests.rb', line 163

def quit
  send_request_for quit: Api::RequestQuit.new
end

#replay_info(replay_path: nil, replay_data: nil, download_data: false) ⇒ Api::ResponseReplayInfo

Returns metadata about a replay file. Does not load the replay. RequestReplayInfo replay_info = 16; //

Parameters:

  • replay_path (String) (defaults to: nil)

    path to replay

  • replay_data (String) (defaults to: nil)

    alternative to file, binary string of replay_file.read

  • download_data (String) (defaults to: false)

    if true, ensure the data and binary are downloaded if this is an old version replay.

Returns:

Raises:



382
383
384
385
386
387
388
389
390
# File 'lib/sc2ai/connection/requests.rb', line 382

def replay_info(replay_path: nil, replay_data: nil, download_data: false)
  raise Sc2::Error, "Missing replay." if replay_data.nil? && replay_path.nil?

  send_request_for replay_info: Api::RequestReplayInfo.new(
    replay_path: replay_path.to_s,
    replay_data: replay_data,
    download_data: download_data
  )
end

#request_quick_loadObject

Loads from an in-memory bookmark.



158
159
160
# File 'lib/sc2ai/connection/requests.rb', line 158

def request_quick_load
  send_request_for quick_load: Api::RequestQuickLoad.new
end

#request_quick_saveObject

Saves game to an in-memory bookmark.



153
154
155
# File 'lib/sc2ai/connection/requests.rb', line 153

def request_quick_save
  send_request_for quick_save: Api::RequestQuickSave.new
end

#restart_gameObject

Single player only. Reinitializes the game with the same player setup.



88
89
90
# File 'lib/sc2ai/connection/requests.rb', line 88

def restart_game
  send_request_for restart_game: Api::RequestRestartGame.new
end

#save_mapObject

Saves binary map data to the local temp directory.



399
400
401
# File 'lib/sc2ai/connection/requests.rb', line 399

def save_map
  send_request_for save_map: Api::RequestSaveMap.new
end

#save_replayObject

Generates a replay.



369
370
371
# File 'lib/sc2ai/connection/requests.rb', line 369

def save_replay
  send_request_for save_replay: Api::RequestSaveReplay.new
end

#send_request_for(**kwargs) ⇒ Object

Sends request for type and returns response that type, i.e.

send_request_for(observation: RequestObservation)

Is identical to

send_request(
  Api::Request.new(observation: RequestObservation)
)[:observation]


423
424
425
426
# File 'lib/sc2ai/connection/requests.rb', line 423

def send_request_for(**kwargs)
  response = send_request(Api::Request.new(**kwargs))
  response.send(kwargs.keys.first)
end

#start_replay(replay_path: nil, replay_data: nil, map_data: nil, record_replay: true, interface_options: {}, **options) ⇒ Object

Given a replay file path or replay file contents, will start the replay

Examples:

Sc2.config do |config|
  config.version = "4.10"
end
Async do
  client = Sc2::ClientManager.obtain(0)
  observer = Sc2::Player::Observer.new
  observer.connect(host: client.host, port: client.port)
  pp observer.api.start_replay(
    replay_path: Pathname("./replays/test.SC2Replay").realpath
  )
  while observer.status == :IN_REPLAY
    #   Step forward
    observer.api.step(1)
    #   fresh observation info
    observation = observer.api.observation
    #   fresh game info
    game_info = observer.api.game_info
  end
ensure
  Sc2::ClientManager.stop(0)
end

Parameters:

  • replay_path (String) (defaults to: nil)

    path to replay

  • replay_data (String) (defaults to: nil)

    alternative to file, binary string of replay_file.read

  • map_data (String) (defaults to: nil)

    optional binary string of SC2 map if not present in paths

  • options (Hash)

    Api:RequestStartReplay options, such as disable_fog, observed_player_id, map_data

  • interface_options (Hash) (defaults to: {})

Raises:



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
# File 'lib/sc2ai/connection/requests.rb', line 120

def start_replay(replay_path: nil, replay_data: nil, map_data: nil, record_replay: true, interface_options: {}, **options)
  raise Sc2::Error, "Missing replay." if replay_data.nil? && replay_path.nil?

  interface_options ||= {}
  send_request_for start_replay: Api::RequestStartReplay.new(
    replay_path: replay_path.to_s,
    replay_data: replay_data,
    map_data: map_data,
    realtime: false,
    disable_fog: true,
    record_replay: record_replay,
    observed_player_id: 0,
    options: Api::InterfaceOptions.new(
      raw: true,
      score: true,
      feature_layer: feature_layer_interface_options(true),
      show_cloaked: true,
      show_burrowed_shadows: true,
      show_placeholders: true,
      raw_affects_selection: false,
      raw_crop_to_playable_area: true,
      **interface_options
    ),
    **options
  )
end

#step(step_count = 1) ⇒ Object

Advances the game simulation by step_count. Not used in realtime mode. Only constant step size supported - subsequent requests use cache.



277
278
279
280
281
282
283
# File 'lib/sc2ai/connection/requests.rb', line 277

def step(step_count = 1)
  @_cached_request_step ||= {}
  @_cached_request_step[step_count] ||= Api::Request.new(
    step: Api::RequestStep.new(count: step_count)
  ).to_proto
  send_request_and_ignore(@_cached_request_step[step_count])
end