1
  2
  3
  4
  5
  6
  7
  8
  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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#![warn(missing_docs)]
#![recursion_limit = "1024"]

//! StarCraft II API for Rust
//!
//! this API is intended to provide functionality similar to that of Blizzard
//! and Google's [StarCraft II API](https://github.com/Blizzard/s2client-api)

#[macro_use]
extern crate error_chain;

extern crate bytes;
extern crate organelle;
extern crate ctrlc;
extern crate futures;
extern crate glob;
extern crate nalgebra as na;
extern crate protobuf;
extern crate rand;
extern crate regex;
extern crate sc2_proto;
extern crate tokio_core;
extern crate tokio_timer;
extern crate tokio_tungstenite;
extern crate tungstenite;
extern crate url;
extern crate uuid;

mod agent;
mod client;
mod computer;
mod ctrlc_breaker;
mod data;
mod frame;
mod instance;
mod launcher;
mod melee;
mod observer;

use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;

use futures::sync::mpsc::{ Sender };
use url::Url;
use uuid::Uuid;

pub use self::agent::{ AgentCell };
pub use self::client::{ ClientRequest, ClientResult };
pub use self::computer::{ ComputerCell };
pub use self::ctrlc_breaker::{ CtrlcBreakerCell };
pub use self::data::{
    Color,
    Rect,
    Rect2,
    Point2,
    Point3,
    Vector2,
    Vector3,

    UnitType,
    Ability,
    Upgrade,
    Buff,
    Alliance,

    ActionTarget,
    Tag,
    ImageData,
    Visibility,
    BuffData,
    UpgradeData,
    AbilityData,
    Effect,
    UnitTypeData,
    Score,
    Unit,
    TerrainInfo,
    PowerSource,
    DisplayType,
    SpatialAction,
    Action,
    Map,
    GamePorts,
    PortSet,
    PlayerSetup,
    GameSettings,
    Race,
    Difficulty,
};
pub use self::frame::{
    FrameData,
    Command,
    DebugCommand,
    DebugTextTarget,
    MapState,
    GameEvent,
    GameState,
    GameData,
};
pub use self::launcher::{ LauncherCell, LauncherSettings };
pub use self::melee::{ MeleeSuite, MeleeSettings, MeleeCell };

error_chain! {
    links {
        Organelle(organelle::Error, organelle::ErrorKind) #[doc="organelle glue"];
    }
    foreign_links {
        Io(std::io::Error) #[doc="link io errors"];
        UrlParseError(url::ParseError) #[doc="link to url parse errors"];
        Protobuf(protobuf::ProtobufError) #[doc="link to protobuf errors"];
    }
    errors {
        /// exe was not supplied to the coordinator
        ExeNotSpecified {
            description("exe not specified")
            display("StarCraft II exe was not specified")
        }
        /// exe supplied to the coordinator does not exist
        ExeDoesNotExist(exe: PathBuf) {
            description("exe file does not exist")
            display("StarCraft II exe does not exist at {:?}", exe)
        }

        /// client failed to open connection to the game instance
        ClientOpenFailed {
            description("unable to open connection to the game instance")
            display("client open failed")
        }
        /// client failed to send a message to the game instance
        ClientSendFailed {
            description("unable to send message to the game instance")
            display("client send failed")
        }
        /// client failed to receive a message from the game instance
        ClientRecvFailed {
            description("unable to receive message from game instance")
            display("client recv failed")
        }
        /// client failed to initiate close handshake
        ClientCloseFailed {
            description("unable to initiate close handshake")
            display("client close failed")
        }

        /// errors received from game instance
        GameErrors(errors: Vec<String>) {
            description("errors in game response")
            display("received errors: {:?}", errors)
        }
        /// an error occurred in agent callback
        AgentError {
            description("error occurred in agent callback")
            display("error occurred in agent callback")
        }

        /// invalid protobuf data from game instance
        InvalidProtobuf(msg: String) {
            description("unable to convert protobuf data to game data")
            display("unable to convert protobuf data: {}", msg)
        }
    }
}

trait FromProto<T> where Self: Sized {
    /// convert from protobuf data
    fn from_proto(p: T) -> Result<Self>;
}

trait IntoSc2<T> {
    fn into_sc2(self) -> Result<T>;
}

impl<T, U> IntoSc2<U> for T where U: FromProto<T> {
    fn into_sc2(self) -> Result<U> {
        U::from_proto(self)
    }
}

trait IntoProto<T> {
    /// convert into protobuf data
    fn into_proto(self) -> Result<T>;
}

/// the messages that can be sent between Sc2 capable
#[derive(Debug)]
pub enum Message {
    /// get instances pool
    GetInstancePool,
    /// get the ports pool
    GetPortsPool,
    /// launch an instance
    LaunchInstance,
    /// the pool of instances to choose from
    InstancePool(HashMap<Uuid, (Url, PortSet)>),
    /// the pool of game ports to choose from (num_instances / 2)
    PortsPool(Vec<GamePorts>),

    /// allow a cell to take complete control of an instance
    ProvideInstance(Uuid, Url),

    /// attempt to connect to instance
    ClientAttemptConnect(Url),
    /// internal-use client successfully connected to instance
    ClientConnected(Sender<tungstenite::Message>),
    /// internal-use client received a message
    ClientReceive(tungstenite::Message),
    /// send some request to the game instance
    ClientRequest(ClientRequest),
    /// result of transaction with game instance
    ClientResult(ClientResult),
    /// internal-use message used to indicate when a transaction has timed out
    ClientTimeout(Uuid),
    /// disconnect from the instance
    ClientDisconnect,
    /// client has closed
    ClientClosed,
    /// client encountered a websocket error
    ClientError(Rc<Error>),

    /// agent is ready for a game to begin
    Ready,

    /// request player setup
    RequestPlayerSetup(GameSettings),
    /// respond with player setup
    PlayerSetup(PlayerSetup),

    /// create a game with the given settings and list of participants
    CreateGame(GameSettings, Vec<PlayerSetup>),
    /// game was created with the given settings
    GameCreated,
    /// notify agents that game is ready to join with the given player setup
    GameReady(PlayerSetup, Option<GamePorts>),
    /// join an existing game
    JoinGame(GamePorts),
    /// fetch the game data
    FetchGameData,
    /// game data ready
    GameDataReady,
    /// request update interval from player
    RequestUpdateInterval,
    /// respond with update interval in game steps
    UpdateInterval(u32),
    /// game started
    GameStarted,

    /// observe the game state
    Observe,
    /// current game state
    Observation(Rc<FrameData>),
    /// issue a command to the game instance
    Command(Command),
    /// issue a debug command to the game instance
    DebugCommand(DebugCommand),
    /// notify the stepper that the cell is done updating
    UpdateComplete,

    /// game ended
    GameEnded,
}

#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
/// defines the roles that govern how connections between cells are made
pub enum Role {
    /// launches new game instances or kills them
    Launcher,
    /// broadcasts idle instances
    InstancePool,
    /// provides instances to clients
    InstanceProvider,

    /// controls agents or observer
    Controller,
    /// provides agent interface to bots
    Agent,
    /// provides client interface to agents or observers
    Client,
    /// observes game state
    Observer,
}

/// type alias for an Sc2 Organelle
pub type Organelle = organelle::Organelle<Message, Role>;
/// type alias for an Sc2 Soma
pub type Soma = organelle::Soma<Message, Role>;
/// type alias for an Sc2 Eukaryote
pub type Eukaryote<T> = organelle::Eukaryote<Message, Role, T>;