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:



249
250
251
252
253
# File 'lib/sc2ai/connection/requests.rb', line 249

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:



397
398
399
# File 'lib/sc2ai/connection/requests.rb', line 397

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:



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

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:



414
415
416
417
418
# File 'lib/sc2ai/connection/requests.rb', line 414

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

#game_infoApi::ResponseGameInfo

Static data about the current game and map.



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

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.



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

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

#observation(game_loop: nil) ⇒ Api::ResponseObservation

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)

Returns:



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

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:



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

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.



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

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.



407
408
409
# File 'lib/sc2ai/connection/requests.rb', line 407

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:



294
295
296
297
298
299
300
301
# File 'lib/sc2ai/connection/requests.rb', line 294

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:



319
320
321
322
323
324
325
326
327
# File 'lib/sc2ai/connection/requests.rb', line 319

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:



333
334
335
336
337
338
339
340
341
342
# File 'lib/sc2ai/connection/requests.rb', line 333

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



350
351
352
353
354
355
356
357
358
# File 'lib/sc2ai/connection/requests.rb', line 350

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:



306
307
308
309
310
311
312
313
# File 'lib/sc2ai/connection/requests.rb', line 306

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:



363
364
365
366
367
368
369
# File 'lib/sc2ai/connection/requests.rb', line 363

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.



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

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:



385
386
387
388
389
390
391
392
393
# File 'lib/sc2ai/connection/requests.rb', line 385

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.



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

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

#request_quick_saveObject

Saves game to an in-memory bookmark.



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

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.



402
403
404
# File 'lib/sc2ai/connection/requests.rb', line 402

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

#save_replayObject

Generates a replay.



372
373
374
# File 'lib/sc2ai/connection/requests.rb', line 372

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]


426
427
428
429
# File 'lib/sc2ai/connection/requests.rb', line 426

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
  observer.disconnect
  Sc2::ClientManager.stop(0)
end.wait

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:



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

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.



279
280
281
282
283
284
285
286
# File 'lib/sc2ai/connection/requests.rb', line 279

def step(step_count = 1)
  step_count = step_count.to_i
  @_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