sc2ai 0.5.0 → 0.6.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 +4 -4
- data/data/sc2ai/protocol/data.proto +1 -1
- data/data/sc2ai/protocol/raw.proto +1 -1
- data/docker_build/Dockerfile.aiarenabot +12 -0
- data/docker_build/Dockerfile.ruby +2 -2
- data/docker_build/docker-compose-base-image.yml +0 -1
- data/docker_build/docker-compose-ladderzip.yml +0 -1
- data/docker_build/docker-compose-versus-bot.yml +15 -0
- data/lib/sc2ai/api/data.rb +59 -1
- data/lib/sc2ai/cli/cli.rb +7 -47
- data/lib/sc2ai/cli/ladderzip.rb +6 -1
- data/lib/sc2ai/cli/versus_bot.rb +305 -0
- data/lib/sc2ai/connection/requests.rb +4 -1
- data/lib/sc2ai/local_play/client_manager.rb +3 -1
- data/lib/sc2ai/local_play/match.rb +2 -2
- data/lib/sc2ai/player/game_state.rb +2 -14
- data/lib/sc2ai/player/geo.rb +19 -2
- data/lib/sc2ai/player/units.rb +12 -8
- data/lib/sc2ai/player.rb +29 -7
- data/lib/sc2ai/protocol/extensions/player_common.rb +25 -0
- data/lib/sc2ai/protocol/extensions/position.rb +1 -1
- data/lib/sc2ai/protocol/extensions/unit.rb +59 -14
- data/lib/sc2ai/protocol/extensions/unit_type_data.rb +30 -0
- data/lib/sc2ai/unit_group/filter_ext.rb +23 -2
- data/lib/sc2ai/version.rb +1 -1
- data/lib/templates/ladderzip/bin/ladder.tt +1 -7
- data/lib/templates/ladderzip/ladderbots.json.tt +11 -0
- data/lib/templates/new/.ladderignore +1 -1
- data/lib/templates/new/api/data.proto +1 -1
- data/lib/templates/new/api/raw.proto +1 -1
- data/lib/templates/new/run_example_match.rb.tt +1 -1
- data/sig/sc2ai.rbs +152 -86
- metadata +17 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a52a7a32bf219355f194817695d5505e47a897da5f3023ff856f4c88a66cc4d0
|
4
|
+
data.tar.gz: 94a565f3dc3c3f43896559664961242a9c06b30429bb41345183eb3fd680f29c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8275c1840c4c7a9548a08ccf90215d41d8eabea15f7915109221f0c8c1deba96847103fc7f552927304c23b702a53aab5f091d8af7a52da33d7d0c419cfe2369
|
7
|
+
data.tar.gz: c547597a3bfb0babfbc2765c0d9fb6af7fab9137077faaf6f419ff51cad0cdfc30dd9816bab8383e9559b1cabc88aa5ec908aaf66348e3ac5570192708c23959
|
@@ -89,7 +89,7 @@ message UnitTypeData {
|
|
89
89
|
optional float sight_range = 25; // Range unit reveals vision.
|
90
90
|
|
91
91
|
repeated uint32 tech_alias = 21 [packed=false]; // Other units that satisfy the same tech requirement.
|
92
|
-
optional uint32 unit_alias = 22
|
92
|
+
optional uint32 unit_alias = 22; // The morphed variant of this unit.
|
93
93
|
|
94
94
|
optional uint32 tech_requirement = 23; // Structure required to build this unit. (Or any with the same tech_alias)
|
95
95
|
optional bool require_attached = 24; // Whether tech_requirement is an add-on.
|
@@ -4,7 +4,7 @@ LABEL service="bot-ruby-local"
|
|
4
4
|
USER root
|
5
5
|
WORKDIR /root/ruby-builder
|
6
6
|
|
7
|
-
ARG RUBY_VERSION=3.4.
|
7
|
+
ARG RUBY_VERSION=3.4.2
|
8
8
|
ARG DEBIAN_DISABLE_RUBYGEMS_INTEGRATION=true
|
9
9
|
|
10
10
|
# Deps - Ruby build
|
@@ -65,7 +65,7 @@ RUN /root/ruby-builder/.ruby/bin/bundle config set --global without test develop
|
|
65
65
|
RUN apt-get update
|
66
66
|
RUN apt-get purge $(aptitude search '~i!~M!~prequired!~pimportant!~R~prequired!~R~R~prequired!~R~pimportant!~R~R~pimportant!busybox!grub!initramfs-tools' | awk '{print $2}') --assume-yes
|
67
67
|
RUN apt-get purge aptitude --assume-yes
|
68
|
-
RUN apt-get install zip openssl build-essential
|
68
|
+
RUN apt-get install zip openssl build-essential libffi-dev --assume-yes
|
69
69
|
RUN apt-get autoremove --assume-yes
|
70
70
|
RUN apt-get clean --assume-yes
|
71
71
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
services:
|
2
|
+
versus_bot:
|
3
|
+
container_name: versus_bot_container
|
4
|
+
image: dysonreturns/arenaclient-bot:local
|
5
|
+
environment:
|
6
|
+
- CPPFLAGS=-DPNG_ARM_NEON_OPT=0
|
7
|
+
platform: "linux/amd64"
|
8
|
+
tty: true
|
9
|
+
build:
|
10
|
+
dockerfile: ./Dockerfile.aiarenabot
|
11
|
+
ports:
|
12
|
+
- "${SERVER_BASE_PORT}:${SERVER_BASE_PORT}/udp"
|
13
|
+
- "${SERVER_GAME_PORT}:${SERVER_GAME_PORT}"
|
14
|
+
- "${CLIENT_BASE_PORT}:${CLIENT_BASE_PORT}/udp"
|
15
|
+
- "${CLIENT_GAME_PORT}:${CLIENT_GAME_PORT}"
|
data/lib/sc2ai/api/data.rb
CHANGED
@@ -108,6 +108,8 @@ module Sc2
|
|
108
108
|
correct_unit_type_costs
|
109
109
|
correct_unit_type_sum
|
110
110
|
decorate_unit_type_placement_length
|
111
|
+
decorate_missing_values
|
112
|
+
decorate_weapon_helpers
|
111
113
|
end
|
112
114
|
|
113
115
|
# @private
|
@@ -256,9 +258,11 @@ module Sc2
|
|
256
258
|
end
|
257
259
|
end
|
258
260
|
|
261
|
+
private
|
262
|
+
|
259
263
|
# @private
|
260
264
|
# Adds placement_length to units if applicable
|
261
|
-
|
265
|
+
def decorate_unit_type_placement_length
|
262
266
|
@units.each_value do |unit_type_data|
|
263
267
|
unit_type_data.placement_length = 0
|
264
268
|
next unless unit_type_data.ability_id
|
@@ -269,5 +273,59 @@ module Sc2
|
|
269
273
|
end
|
270
274
|
end
|
271
275
|
end
|
276
|
+
|
277
|
+
# @private
|
278
|
+
# Adds values missing from the API
|
279
|
+
def decorate_missing_values
|
280
|
+
# Battlecruiser has no weapons. Force these in by hand.
|
281
|
+
@units[Api::UnitTypeId::BATTLECRUISER].weapons = [
|
282
|
+
Api::Weapon.new(
|
283
|
+
type: Api::Weapon::TargetType::GROUND,
|
284
|
+
damage: 8.0,
|
285
|
+
damage_bonus: [],
|
286
|
+
attacks: 1,
|
287
|
+
range: 6.0,
|
288
|
+
speed: 0.224
|
289
|
+
),
|
290
|
+
Api::Weapon.new(
|
291
|
+
type: Api::Weapon::TargetType::AIR,
|
292
|
+
damage: 5.0,
|
293
|
+
damage_bonus: [],
|
294
|
+
attacks: 1,
|
295
|
+
range: 6.0,
|
296
|
+
speed: 0.224
|
297
|
+
)
|
298
|
+
]
|
299
|
+
end
|
300
|
+
|
301
|
+
# @private
|
302
|
+
# Adds ground_damage, air_damage, ground_range, air_range, ground_dps and air_dps
|
303
|
+
def decorate_weapon_helpers
|
304
|
+
@units.each do |unit_type_id, unit_type_data|
|
305
|
+
ground_weapon = unit_type_data.weapons.find do |weapon|
|
306
|
+
weapon.type == :GROUND || weapon.type == :ANY
|
307
|
+
end
|
308
|
+
|
309
|
+
air_weapon = unit_type_data.weapons.find do |weapon|
|
310
|
+
weapon.type == :AIR || weapon.type == :ANY
|
311
|
+
end
|
312
|
+
|
313
|
+
unit_type_data.ground_range = ground_weapon&.range || 0.0
|
314
|
+
unit_type_data.air_range = air_weapon&.range || 0.0
|
315
|
+
|
316
|
+
ground_attacks = ground_weapon&.attacks || 0
|
317
|
+
air_attacks = air_weapon&.attacks || 0
|
318
|
+
base_ground_damage = ground_weapon&.damage || 0.0
|
319
|
+
base_air_damage = air_weapon&.damage || 0.0
|
320
|
+
ground_attack_speed = ground_weapon&.speed || 1.0
|
321
|
+
air_attack_speed = air_weapon&.speed || 1.0
|
322
|
+
|
323
|
+
unit_type_data.ground_damage = base_ground_damage * ground_attacks
|
324
|
+
unit_type_data.air_damage = base_air_damage * air_attacks
|
325
|
+
|
326
|
+
unit_type_data.ground_dps = unit_type_data.ground_damage / ground_attack_speed
|
327
|
+
unit_type_data.air_dps = unit_type_data.air_damage / air_attack_speed
|
328
|
+
end
|
329
|
+
end
|
272
330
|
end
|
273
331
|
end
|
data/lib/sc2ai/cli/cli.rb
CHANGED
@@ -2,6 +2,7 @@ require "thor"
|
|
2
2
|
|
3
3
|
require "sc2ai/cli/new"
|
4
4
|
require "sc2ai/cli/ladderzip"
|
5
|
+
require "sc2ai/cli/versus_bot"
|
5
6
|
|
6
7
|
module Sc2
|
7
8
|
# Command line utilities
|
@@ -16,36 +17,22 @@ module Sc2
|
|
16
17
|
|
17
18
|
register(Sc2::Cli::New, "new", "new BOTNAME RACE", "Creates botname folder with bot template")
|
18
19
|
register(Sc2::Cli::Ladderzip, "ladderzip", "ladderzip BOTNAME", "Prepares a zip via docker for supplied aiarena bot name")
|
20
|
+
register(Sc2::Cli::VersusBot, "vsbot", "vsbot FILENAME", "Copies target zip to docker and starts a match")
|
19
21
|
|
20
22
|
# TODO: Use thor's #ask and other methods helpers of raw $stdin and puts.
|
21
23
|
desc "setup410", "Downloads and install SC2 v4.10"
|
22
24
|
# downloads and install SC2 v4.10
|
23
25
|
def setup410
|
24
|
-
say " "
|
25
|
-
say "This script sets up SC2 at version 4.10, which we use competitively."
|
26
|
-
say "Press any key to continue..."
|
27
|
-
ask ""
|
28
|
-
|
29
|
-
say "You must accept the Blizzard® StarCraft® II AI and Machine Learning License at"
|
30
|
-
say "https://blzdistsc2-a.akamaihd.net/AI_AND_MACHINE_LEARNING_LICENSE.html"
|
31
|
-
say "It is PERMISSIVE and grants you freedoms over the standard EULA."
|
32
|
-
say "We do not record this action, but depend on software goverend by that license to continue."
|
33
|
-
puts 'If you accept, type "iagreetotheeula" (without quotes) and press ENTER to continue:'
|
34
|
-
|
35
|
-
while $stdin.gets.chomp != "iagreetotheeula"
|
36
|
-
say ""
|
37
|
-
puts 'Type "iagreetotheeula" (without quotes) and press ENTER to continue:'
|
38
|
-
end
|
39
|
-
say ""
|
40
|
-
say ""
|
41
|
-
say "Great decision."
|
42
|
-
|
43
26
|
require "sc2ai"
|
44
27
|
Async do
|
45
28
|
Sc2.logger.level = :fatal
|
29
|
+
say " "
|
30
|
+
say "This script sets up SC2 at version 4.10, which we use competitively."
|
46
31
|
say "SC2 will launch a blank window, be unresponsive, but download about 100mb in the background."
|
32
|
+
say ""
|
33
|
+
say "It will appear to hang as it updates. This is normal."
|
47
34
|
say "Let it finish and close itself."
|
48
|
-
say "Press
|
35
|
+
say "Press any key to continue..."
|
49
36
|
ask ""
|
50
37
|
|
51
38
|
say ""
|
@@ -110,23 +97,6 @@ module Sc2
|
|
110
97
|
Sc2.logger.info " interface_options: #{$bot.interface_options}"
|
111
98
|
end
|
112
99
|
|
113
|
-
# desc "ladderzip", "Uses docker to cross-compile a compatible binary for the ladder"
|
114
|
-
# def ladderzip
|
115
|
-
# end
|
116
|
-
|
117
|
-
# desc "ladderquickzip", "Ladder zip, if you have no additional gems installed."
|
118
|
-
# long_desc <<-LONGDESC
|
119
|
-
# `sc2ai ladderzip_basic` will download a portable ruby and gems, then copy your bot files into a zip file.
|
120
|
-
#
|
121
|
-
# If you have added any gems which need compiling, this option is not for you.
|
122
|
-
# Any gems built with native extensions will likely not be targeting the linux platform and be nonfunctional.
|
123
|
-
#
|
124
|
-
# For a cross-platform build, use ladderzip instead. This will launch a Docker container to compile a zip for you.
|
125
|
-
# LONGDESC
|
126
|
-
# def ladderquickzip
|
127
|
-
# raise Sc2::Error, "Not yet implemented."
|
128
|
-
# end
|
129
|
-
|
130
100
|
desc "laddermatch", "Joins a ladder match as per aiarena spec"
|
131
101
|
option :GamePort, required: true, desc: "SC2 port. Corresponds to SC2 launch option '-port'"
|
132
102
|
option :LadderServer, required: true, desc: "SC2 server ip or hostname"
|
@@ -162,16 +132,6 @@ module Sc2
|
|
162
132
|
$bot.play
|
163
133
|
end.wait
|
164
134
|
end
|
165
|
-
|
166
|
-
# desc "install APP_NAME", "install one of the available apps" # [4]
|
167
|
-
# method_options :force => :boolean, :alias => :string # [5]
|
168
|
-
# def install(name)
|
169
|
-
# user_alias = options[:alias]
|
170
|
-
# if options.force?
|
171
|
-
# # do something
|
172
|
-
# end
|
173
|
-
# # other code
|
174
|
-
# end
|
175
135
|
end
|
176
136
|
# standard:enable Style/GlobalVars
|
177
137
|
end
|
data/lib/sc2ai/cli/ladderzip.rb
CHANGED
@@ -78,8 +78,12 @@ module Sc2
|
|
78
78
|
# create_link("./.build/#{botname}", "./bin/ladder")
|
79
79
|
end
|
80
80
|
|
81
|
+
def create_ladderbots_json
|
82
|
+
template("ladderzip/ladderbots.json", "./.build/ladderbots.json")
|
83
|
+
end
|
84
|
+
|
81
85
|
def start_container
|
82
|
-
cmd = "docker compose -f #{@compose_file} up
|
86
|
+
cmd = "docker compose -f #{@compose_file} up versus_bot -d --force-recreate"
|
83
87
|
Kernel.system(cmd)
|
84
88
|
end
|
85
89
|
|
@@ -95,6 +99,7 @@ module Sc2
|
|
95
99
|
ignore_pattern -= include_pattern
|
96
100
|
|
97
101
|
include_pattern.collect! { |pt| pt.delete_prefix("!") }
|
102
|
+
include_pattern.collect! { |pt| pt + "**/*" if pt.ends_with?("/") }
|
98
103
|
|
99
104
|
files = Dir.glob("**/*")
|
100
105
|
|
@@ -0,0 +1,305 @@
|
|
1
|
+
module Sc2
|
2
|
+
# Command line utilities
|
3
|
+
class Cli < Thor
|
4
|
+
# Command line action allowing your bot to play vs a ladder bot via docker
|
5
|
+
class VersusBot < Thor::Group
|
6
|
+
# standard:disable Style/GlobalVars
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
# Define arguments and options
|
10
|
+
argument :filename, desc: "Path to zip. Relative or absolute."
|
11
|
+
argument :map, desc: "Absolute path or shot name, i.e. 'ThunderbirdAIE', 'ThunderbirdAIE.SC2Map'.", default: "ThunderbirdAIE"
|
12
|
+
desc "Sends a bot zip to the ladder and makes it play against us"
|
13
|
+
|
14
|
+
def get_botname
|
15
|
+
@enemy_botname = File.basename(filename, ".zip")
|
16
|
+
end
|
17
|
+
|
18
|
+
def docker_exists
|
19
|
+
if Kernel.system("docker --version", out: File::NULL, err: File::NULL)
|
20
|
+
say_status("docker", "found", :green)
|
21
|
+
else
|
22
|
+
say_status("docker", "not found", :red)
|
23
|
+
say("Please install 'docker' and/or ensure it's in your PATH to continue.")
|
24
|
+
raise SystemExit
|
25
|
+
end
|
26
|
+
|
27
|
+
if Kernel.system("docker info", out: File::NULL, err: File::NULL)
|
28
|
+
say_status("docker engine", "found", :green)
|
29
|
+
else
|
30
|
+
say(" ")
|
31
|
+
say_status("docker engine", "offline", :red)
|
32
|
+
say("Please start docker engine. If you have Docker Desktop installed, open it before retrying.")
|
33
|
+
raise SystemExit
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def bot_validation
|
38
|
+
if Pathname("./boot.rb").exist?
|
39
|
+
say_status("detected boot.rb", "found", :green)
|
40
|
+
else
|
41
|
+
say_status("detected boot.rb", "not found", :red)
|
42
|
+
raise Sc2::Error, "boot.rb not found. Bot started from wrong directory."
|
43
|
+
end
|
44
|
+
|
45
|
+
ENV.delete("AIARENA")
|
46
|
+
require "./boot"
|
47
|
+
|
48
|
+
if $bot.is_a? Sc2::Player
|
49
|
+
say_status("instance $bot", $bot.class, :green)
|
50
|
+
else
|
51
|
+
say_status("instance $bot", $bot.class, :red)
|
52
|
+
raise Sc2::Error, "$bot instance nil or not Sc2::Player. Review boot.rb to ensure it creates $bot."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.source_paths
|
57
|
+
[Pathname(".").expand_path.to_s]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.source_root
|
61
|
+
Paths.template_root.to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
def port_configuration
|
65
|
+
# Start with basic port setup
|
66
|
+
@port_config = Sc2::Ports.port_config_auto(num_players: 2)
|
67
|
+
say_status("port config", "configured", :green)
|
68
|
+
ENV["SERVER_BASE_PORT"] = @port_config.server_port_set.base_port.to_s
|
69
|
+
ENV["SERVER_GAME_PORT"] = @port_config.server_port_set.game_port.to_s
|
70
|
+
ENV["CLIENT_BASE_PORT"] = @port_config.client_port_sets.first.base_port.to_s
|
71
|
+
ENV["CLIENT_GAME_PORT"] = @port_config.client_port_sets.first.game_port.to_s
|
72
|
+
|
73
|
+
# @port_config_env =
|
74
|
+
# "-e SERVER_BASE_PORT=#{@port_config.server_port_set.base_port} " +
|
75
|
+
# "-e SERVER_GAME_PORT=#{@port_config.server_port_set.game_port} " +
|
76
|
+
# "-e CLIENT_BASE_PORT=#{@port_config.client_port_sets.first.base_port} " +
|
77
|
+
# "-e CLIENT_GAME_PORT=#{@port_config.client_port_sets.first.game_port} "
|
78
|
+
|
79
|
+
# # Create a modified version of ports for docker bot
|
80
|
+
# @client_port_config = @port_config.dup
|
81
|
+
# # Adjust ports for docker
|
82
|
+
# @client_port_config.client_port_sets[0].base_port = 8453
|
83
|
+
# @client_port_config.client_port_sets[0].game_port = 2359
|
84
|
+
#
|
85
|
+
# # Get dynamic assigned ports for host
|
86
|
+
# real_client_base_port = `docker port versus_bot_container 8453/udp`.strip.split(":").last
|
87
|
+
# real_client_game_port = `docker port versus_bot_container 2359`.strip.split(":").last
|
88
|
+
# # Fix our client ports to forward to the docker bot
|
89
|
+
# @port_config.client_port_sets[0].base_port = real_client_base_port.to_i
|
90
|
+
# @port_config.client_port_sets[0].game_port = real_client_game_port.to_i
|
91
|
+
end
|
92
|
+
|
93
|
+
def set_compose_file
|
94
|
+
say_status("docker", "build local play container", :cyan)
|
95
|
+
@compose_file = Sc2::Paths.gem_root
|
96
|
+
.join("docker_build", "docker-compose-versus-bot.yml")
|
97
|
+
.expand_path.to_s
|
98
|
+
@compose_args = "-f #{@compose_file} #{@port_config_env}"
|
99
|
+
cmd = "docker compose #{@compose_args} build"
|
100
|
+
Kernel.system(cmd)
|
101
|
+
end
|
102
|
+
|
103
|
+
def versus_bot_validation
|
104
|
+
if Pathname(filename).exist?
|
105
|
+
say_status("checking zip #{filename}", "found", :green)
|
106
|
+
else
|
107
|
+
say_status("checking zip #{filename}", "not found", :red)
|
108
|
+
raise Sc2::Error, "#{filename} not found. Try an absolute path if relative will not detect."
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def contains_ladderbot_json
|
113
|
+
# Max size of the portion where zip filenames are stored
|
114
|
+
max_end_of_cd_size = (1 << 16) - 1 + 22 + 20
|
115
|
+
searching_for_file = "ladderbots.json" # what we want to find
|
116
|
+
found = File.open(Pathname(filename).realpath, "rb") do |f|
|
117
|
+
# Borrowed code, will search near the end of a zip
|
118
|
+
if f.size > max_end_of_cd_size
|
119
|
+
f.seek(-max_end_of_cd_size, IO::SEEK_END)
|
120
|
+
end
|
121
|
+
content = f.read
|
122
|
+
content.include?(searching_for_file)
|
123
|
+
end
|
124
|
+
|
125
|
+
if found
|
126
|
+
say_status("zip contains ladderbots.json", "found", :green)
|
127
|
+
else
|
128
|
+
say_status("zip contains ladderbots.json", "not found", :red)
|
129
|
+
puts "#{filename} should contain ladderbots.json. Add the file to the zip and try again."
|
130
|
+
raise SystemExit
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def start_container
|
135
|
+
say_status("docker", "starting bot container", :cyan)
|
136
|
+
cmd = "docker compose #{@compose_args} up versus_bot -d" ## { rand(20) == 0 ? "--force-recreate" : "" }
|
137
|
+
Kernel.system(cmd)
|
138
|
+
end
|
139
|
+
|
140
|
+
def copy_opponent_zip
|
141
|
+
say_status("docker", "copying opponent...", :cyan)
|
142
|
+
cmd = "docker compose #{@compose_args} cp #{Pathname(filename).realpath} versus_bot:/bots/"
|
143
|
+
Kernel.system(cmd)
|
144
|
+
end
|
145
|
+
|
146
|
+
def extract_opponent
|
147
|
+
say_status("docker", "extracting opponent...", :cyan)
|
148
|
+
cmd = "docker compose #{@compose_args} exec --workdir /bots versus_bot unzip -o #{Pathname(filename).basename} -d #{@enemy_botname}"
|
149
|
+
Kernel.system(cmd)
|
150
|
+
end
|
151
|
+
|
152
|
+
def pull_ladderbots_json
|
153
|
+
require "tempfile"
|
154
|
+
|
155
|
+
say_status("docker", "pulling enemy 'ladderbots.json'", :cyan)
|
156
|
+
|
157
|
+
@temp_path = Tempfile.new("enemy_ladderbots.json").path
|
158
|
+
|
159
|
+
cmd = "docker compose #{@compose_args} cp versus_bot:/bots/#{@enemy_botname}/ladderbots.json #{@temp_path}"
|
160
|
+
Kernel.system(cmd)
|
161
|
+
if File.exist?(@temp_path)
|
162
|
+
say_status("docker", "pulled 'ladderbots.json' ok", :green)
|
163
|
+
else
|
164
|
+
say_status("docker", "error pulling 'ladderbots.json'", :red)
|
165
|
+
exit
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def parse_enemy_config
|
170
|
+
say_status("docker", "configuring enemy", :cyan)
|
171
|
+
json = JSON.parse(File.read(@temp_path))
|
172
|
+
bot_json = json["Bots"][@enemy_botname]
|
173
|
+
@enemy_runtime = bot_json["Type"].downcase # "BinaryCpp",
|
174
|
+
@enemy_race = bot_json["Race"].upcase.to_sym
|
175
|
+
@enemy_filename = bot_json["FileName"]
|
176
|
+
@command = case @enemy_runtime
|
177
|
+
when "cppwin32"
|
178
|
+
"wine #{@enemy_filename}.exe"
|
179
|
+
when "cpplinux", "binarycpp"
|
180
|
+
"/bots/#{@enemy_filename}/#{@enemy_filename}"
|
181
|
+
when "dotnetcore"
|
182
|
+
"dotnet #{@enemy_filename}.dll"
|
183
|
+
when "java"
|
184
|
+
"java -jar #{@enemy_filename}.jar"
|
185
|
+
when "nodejs"
|
186
|
+
"node #{@enemy_filename}.js"
|
187
|
+
when "python"
|
188
|
+
"python run.py"
|
189
|
+
else
|
190
|
+
@enemy_filename
|
191
|
+
end
|
192
|
+
|
193
|
+
# Make executable
|
194
|
+
if %w[cpplinux binarycpp].include?(@enemy_runtime)
|
195
|
+
cmd = "docker compose #{@compose_args} exec --workdir /bots/#{@enemy_botname} versus_bot chmod +x #{@command}"
|
196
|
+
Kernel.system(cmd)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Make data dir
|
200
|
+
say_status("docker", "ensure data directory exists", :cyan)
|
201
|
+
cmd = "docker compose #{@compose_args} exec --workdir /bots/#{@enemy_botname} versus_bot sh -c \"mkdir data && chmod +w data\""
|
202
|
+
Kernel.system(cmd)
|
203
|
+
end
|
204
|
+
|
205
|
+
def run_match
|
206
|
+
Async do
|
207
|
+
Sc2.logger.level = :info
|
208
|
+
|
209
|
+
client = Sc2::ClientManager.obtain(0)
|
210
|
+
enemy_client = Sc2::ClientManager.obtain(1)
|
211
|
+
|
212
|
+
if Gem.win_platform?
|
213
|
+
sleep(10)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Ladder specific overrides
|
217
|
+
$bot.realtime = false
|
218
|
+
$bot.opponent_id = @enemy_botname
|
219
|
+
|
220
|
+
say_status("host", "connecting...", :cyan)
|
221
|
+
$bot.connect(host: client.host, port: client.port)
|
222
|
+
say_status("host", "creating game...", :cyan)
|
223
|
+
$bot.create_game(map: MapFile.new(map.to_s), players: [
|
224
|
+
$bot,
|
225
|
+
Sc2::Player.new(race: @enemy_race, name: @enemy_botname, type: Api::PlayerType::PARTICIPANT)
|
226
|
+
], realtime: false)
|
227
|
+
|
228
|
+
begin
|
229
|
+
Async do
|
230
|
+
say_status("host", "joining game...", :cyan)
|
231
|
+
$bot.join_game(
|
232
|
+
server_host: client.host,
|
233
|
+
port_config: @port_config
|
234
|
+
)
|
235
|
+
$bot.add_listener($bot, klass: Sc2::Connection::StatusListener)
|
236
|
+
$bot.play
|
237
|
+
|
238
|
+
begin
|
239
|
+
safe_player_name = $bot.name.gsub(/\s*[^A-Za-z0-9.-]\s*/, "_").downcase
|
240
|
+
response = $bot.api.save_replay
|
241
|
+
path = Pathname(Paths.bot_data_replay_dir).join("autosave-#{safe_player_name}-vs-#{@enemy_botname}.SC2Replay")
|
242
|
+
f = File.new(path, "wb:ASCII-8BIT")
|
243
|
+
f.write_nonblock(response.data)
|
244
|
+
f.close
|
245
|
+
rescue
|
246
|
+
# Replay save failed
|
247
|
+
end
|
248
|
+
$bot.disconnect
|
249
|
+
end
|
250
|
+
|
251
|
+
Async do
|
252
|
+
say_status("opponent", "joining game...", :cyan)
|
253
|
+
|
254
|
+
# 192... ip
|
255
|
+
# host_ladder_ip = `docker compose #{@compose_args} exec versus_bot sh -c "getent ahostsv4 host.docker.internal | grep STREAM"`.split(" ").first.strip
|
256
|
+
# Local ip
|
257
|
+
# host_ladder_ip = `docker inspect -f '{{range.NetworkSettings.Networks}}{{.Gateway}}{{end}}' versus_bot_container`.strip
|
258
|
+
enemy_command = [@command,
|
259
|
+
# "--LadderServer #{client.host}",
|
260
|
+
# "--RealTime 0"
|
261
|
+
"--LadderServer host.docker.internal",
|
262
|
+
"--GamePort #{enemy_client.port}",
|
263
|
+
"--StartPort #{@port_config.start_port}",
|
264
|
+
"--OpponentId 00000000-0000-0000-0000-000000000000"]
|
265
|
+
.join(" ")
|
266
|
+
|
267
|
+
# cmd = "docker compose #{@compose_args} exec --workdir /bots/#{@enemy_botname} versus_bot bash -c '#{enemy_command}'"
|
268
|
+
cmd = "docker compose #{@compose_args} exec --workdir /bots/#{@enemy_botname} versus_bot bash -c '#{enemy_command}'"
|
269
|
+
|
270
|
+
puts cmd
|
271
|
+
process_options = {}
|
272
|
+
if Gem.win_platform?
|
273
|
+
process_options[:new_pgroup] = true
|
274
|
+
else
|
275
|
+
process_options[:pgroup] = true
|
276
|
+
end
|
277
|
+
# ::Async::Process.spawn(cmd, **process_options)
|
278
|
+
unless Kernel.system(cmd)
|
279
|
+
say_status("opponent", "exited with bad status", :red)
|
280
|
+
raise SystemExit
|
281
|
+
end
|
282
|
+
end
|
283
|
+
rescue => e
|
284
|
+
say_status("error", e.message, :red)
|
285
|
+
raise SystemExit
|
286
|
+
# Replay save failed
|
287
|
+
end
|
288
|
+
end.wait
|
289
|
+
ensure
|
290
|
+
ClientManager.stop_all
|
291
|
+
end
|
292
|
+
|
293
|
+
def stop_container
|
294
|
+
cmd = "docker stop versus_bot_container"
|
295
|
+
Kernel.system(cmd)
|
296
|
+
end
|
297
|
+
|
298
|
+
def bye
|
299
|
+
say "Game over."
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# standard:enable Style/GlobalVars
|
304
|
+
end
|
305
|
+
end
|
@@ -110,8 +110,9 @@ module Sc2
|
|
110
110
|
# game_info = observer.api.game_info
|
111
111
|
# end
|
112
112
|
# ensure
|
113
|
+
# observer.disconnect
|
113
114
|
# Sc2::ClientManager.stop(0)
|
114
|
-
# end
|
115
|
+
# end.wait
|
115
116
|
# @param replay_path [String] path to replay
|
116
117
|
# @param replay_data [String] alternative to file, binary string of replay_file.read
|
117
118
|
# @param map_data [String] optional binary string of SC2 map if not present in paths
|
@@ -195,6 +196,7 @@ module Sc2
|
|
195
196
|
|
196
197
|
# Snapshot of the current game state. Primary source for raw information
|
197
198
|
# @param game_loop [Integer] you wish to wait for (realtime only)
|
199
|
+
# @return [Api::ResponseObservation]
|
198
200
|
def observation(game_loop: nil)
|
199
201
|
# Sc2.logger.debug { "#{self.class}.#{__method__} game_loop: #{game_loop}" }
|
200
202
|
if game_loop.nil?
|
@@ -275,6 +277,7 @@ module Sc2
|
|
275
277
|
# Advances the game simulation by step_count. Not used in realtime mode.
|
276
278
|
# Only constant step size supported - subsequent requests use cache.
|
277
279
|
def step(step_count = 1)
|
280
|
+
step_count = step_count.to_i
|
278
281
|
@_cached_request_step ||= {}
|
279
282
|
@_cached_request_step[step_count] ||= Api::Request.new(
|
280
283
|
step: Api::RequestStep.new(count: step_count)
|
@@ -10,7 +10,7 @@ module Sc2
|
|
10
10
|
|
11
11
|
class << self
|
12
12
|
extend Forwardable
|
13
|
-
def_delegators :instance, :obtain, :get, :start, :stop, :stop_all
|
13
|
+
def_delegators :instance, :obtain, :get, :start, :stop, :stop_all, :host
|
14
14
|
end
|
15
15
|
|
16
16
|
# Gets client for player X or starts an instance
|
@@ -63,6 +63,8 @@ module Sc2
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
attr_accessor :host
|
67
|
+
|
66
68
|
private
|
67
69
|
|
68
70
|
attr_accessor :clients, :ports
|
@@ -68,7 +68,7 @@ module Sc2
|
|
68
68
|
ensure
|
69
69
|
Sc2.logger.debug { "Game over, disconnect players." }
|
70
70
|
# Suppress interrupt errors #$stderr.reopen File.new(File::NULL, "w")
|
71
|
-
player.quit if
|
71
|
+
player.quit if [Paths::PF_WINDOWS, Paths::PF_WSL1, Paths::PF_WSL2].include?(Paths.platform)
|
72
72
|
player.disconnect
|
73
73
|
ClientManager.stop(player_index) # unless keep_clients_alive
|
74
74
|
end
|
@@ -95,7 +95,7 @@ module Sc2
|
|
95
95
|
response = player.api.save_replay
|
96
96
|
path = Pathname(Paths.bot_data_replay_dir).join("autosave-#{safe_player_name}.SC2Replay")
|
97
97
|
f = File.new(path, "wb:ASCII-8BIT")
|
98
|
-
f.
|
98
|
+
f.write_nonblock(response.data)
|
99
99
|
f.close
|
100
100
|
end
|
101
101
|
|