Table of Contents
Definitions below give explanation of terms used in this text. Some explanations may slightly differ from generally-accepted because of domain-specific aspects.
- IL-2 FB
- "Old" version of «IL-2 Sturmovik» aviasimulator. It is often referenced as «IL-2 Sturmovik: Forgotten Battles», however it also implies all further commercial versions up to «IL-2 Sturmovik: 1946» including all official free patches.
- DS
- Dedicated server of IL-2 FB: a stand-alone headless application which is used for creation of a single entry point for multiplayer game mode.
- API
- Application's interface which makes it possible for 3rd-party software to interact with the application.
- Telnet
- A network protocol which provides a bidirectional interactive text-oriented communication facility using a virtual terminal connection.
- Console
- Server's terminal (shell) which is used by server's administrators to manage server by executing text commands and to monitor several aspects of users' activity like connections to DS, chat messages and so on. Console is also an API served by server over Telnet protocol to provide terminal access to 3rd-party software.
- Device Link
- A game-specific network protocol and API which is generally used to access current state of players' aircrafts by 3rd-party software. For DS it provides ability to query coorinates of all actors and static objects in nearly real-time fashion. Refer to documentation for more details.
- Mission
- A text file which contains definition of game environment, objects, actors, targets and so on. Mission is also a process of execution of mission file, i.e. a running game. Refer to demo page of mission parser to get example of mission definition.
- Game Log
- A text file produced by DS which stores in-game events each of which is recorded in an append-only mode. Each event represents a set of changes in state of actors in mission. Refer to demo page of game log parser to get examples of event records.
- Server Config
- A text file which defines server's technical characteristics, facilities and global game options. Refer to configuration editor for more details.
- Streaming
- A process of continuous sharing of data from producer to direct consumer or storage.
- Pub/Sub
- Publish–subscribe is a communication pattern where senders of messages know nothing about message receivers, as communication is provided by a mediator called "message broker" or "message bus". This pattern allows to totally decouple senders from receivers, hence, they do not need to know about existence, location, availability and implementation of each other.
- HTTP
- Request–response communication protocol in the client–server computing model, where messages are presented as structured text. Designed for transfer of hypermedia text (HTML). It is the foundation of any data exchange on the Web.
- REST
- REST (REpresentational State Transfer) is an architectural style, and an approach to communications that is often used in the development of Web services. The key abstraction of information in REST is a resource. Other important thing associated with REST is resource methods to be used to perform the desired transition of resource's state. Usually implemented on top of HTTP, but not limited to it.
- WS
- WebSockets is a communications protocol, providing full-duplex communication channels between a client and a server. It makes it possible to send messages to a server and receive event-driven responses without having to poll the server for a reply. WebSockets protocol was designed to work over HTTP and allows web application to communicate with server directly from web browser.
- NATS
- NATS is a high performance messaging system that acts as a distributed messaging queue for cloud native applications (see more info).
- NATS Streaming
- NATS Streaming is a data streaming system powered by NATS (see more info).
Airbridge is an application which wraps dedicated server of «IL-2 Sturmovik: Forgotten Battles» aviasimulator.
It acts as additional access layer on top of dedicated server and provides high-level API with ability to subscribe to game events. Airbridge makes it possible to communicate with dedicated server by exchanging structured messages instead of raw strings and packages.
This means that you can access server's console, device link and mission storage in a unified way. Also it's possible to subscribe to the stream of parsed game events easily.
Airbridge allows totally remote access to dedicated server without need to bother about access to server's file system. This allows to escape limitations on location of supplementary software and server commanders: dedicated server and 3rd-party software can now run on different machines and under different operating systems.
All that brings much easier server's API and more pleasant development experience.
The main rationale behind this project is a need for convenient unified programmatic access to different facilities of dedicated server along with ability to monitor users' in-game activity and to manage server remotely.
Dedicated server exposes multiple facilities to 3rd-party applications: management console, location service, mission storage, config, streaming of in-game events, etc. All these facilities require different ways of communication and use different data structured for that, which are not documented. This makes it difficult, tedious and error-prone to build systems on top of bare dedicated server, especially server commanders. Developers of every commander have to invent their own toolset for accessing same server's facilities. This results in duplication of code and different implementations for different programming languages.
Airbridge unifies API to server's facilities and uses structured messages for communication instead of raw strings or bytes. It provides API consistency and development comfort. Access to each facility is done via corresponding stand-alone library, e.g. il2fb-ds-middleware, il2fb-game-log-parser, il2fb-mission-parser and il2fb-ds-config. These libraries accumulate almost all knowledge of their subjects and can be used separately. Community can contrubite to their development and free up much of resources by reusing these libraries. Airbridge aggregates These libraries and exposes their functionality on top of a running dedicated server.
Dedicated server allows only one application to access its management console at a time. Moreover, storage of game events (game log) is sticked to server's file system making it impossible to access events outside server. Same is right for mission storage: if missions are genarated by 3rd-party software, they need to be uploaded to server's mission storage, but there is no way to do this. All that results into creation of heavy monolithic applications which combine application's logic, communication with game server and external services like databases, web applications and mission generators into a complex one-stop shop.
Additionally, most of dedicated servers run on dedicated hardware along with other services under Windows OS. This is quite not the best OS for running complex systems and it's definitely not suitable for development of them.
Airbridge allows developes of 3rd-party software to escape single machine and Windows OS giving them ability to bring more power and flexibility to computation, logic and infrastructure of their systems.
The diagram below depicts architecture of Airbridge application for better understanding of its implementation and work principles.
Airbridge application runs dedicated server in background as a coprocess. It captures server's STDOUT with STDIN and forwards it to own STDOUT with STDIN. STDIN of Airbridge is forwarded to server's STDIN. This allows to do analysis and filtering of terminal I/O, e.g. addition of colors for prompt and errors. From user's perspective there's no visible difference between work with bare server and work with Airbridge. This is good for compatibility reasons.
Information about server's config is provided to Airbridge by il2fb-ds-config library. Most important config options are related to console's and device link's ports and location of game log. Location of missions is always known and contained inside server's directory.
Communication between Airbridge and dedicated server is provided by device link and console clients (see il2fb-ds-middleware library). They allow to perform high-level requests as well as to send raw data. The latter one is used to build appropriate proxies on top of clients. Proxies allow existing applications to continue to communicate with server without changes. At the same time new applications can use unified API of Airbridge without any need to bother themselves with knowledge about low-level protocols.
Device link on dedicated server can be used only to locate coordinates of
actors and buildings. As location of objects is done by execution of multiple
requests to server's device link, a radar
is build on top of its client to
simplify location of different types of objects.
Game log of dedicated server is monitored by a game log watcher. If new records appear in game log, the watcher will read them and pass to a game log parser (see il2fb-game-log-parser library). The parser emits structured representation of events. It also emits not parsed strings if it failes to parse them. This can be used to track parsing errors which can occur if a new or unknown event happens. Such events can be stored and used for improving parser.
All features of dedicated server can be separated into two categories: requests and streaming. Requests are made via radar or console client. Streaming is a bit more compticated as events of a single logical facility can come from different physical souces (i.e. events mainly come from game log but can come from console client as well).
There are four logical facilities which bring streaming to their subscribers:
chat
, events
, not parsed strings
and radar
. The first three
facilities act as routers between data sources and subscribers: chat
facility subscribes to chat messages from console client and broadcasts them to
chat subscribers; events
facility subscribes to game events from game log
parser and to user connection events from console client and broadcasts events
to events subscribers; not parsed strings
facility subscribes to strings
produced by game log parser and broadcasts them to own subscribers.
In contrast, radar
facility does not route data from other sources.
Instead, it produces it by querying radar component periodically. Period of
querying depends on the needs of its subscribers.
Subscribers in terms of Airbridge are any objects who follow its subscription interface. Subscribers can be static and dynamic: static subscribers are created when application starts and work until it exits; dynamic subscribers can be created and destroyed at any moment. For example, it's possible to create a file streaming subscriber or NATS streaming subscriber which will work from application's startup till its end. Also it's possible to connect to Airbridge via WebSocket and subscribe to facilities dynamically.
Clients of Airbridge can perform requests via different APIs depending on their needs. They can use Request-Reply API over NATS or REST API over HTTP.
REST API combines two independent parts: API for dedicated server and API for missions storage. In fact, these APIs can be separated from each other and live their independent lives in different services (splitted into microservices), but this does not make sense at this point due to maintenance overhead.
This section provides an overview of features which Airbridge brings to its users. As it was already mentioned in the previous section, all features can be devided into two categories: requests and streaming.
Requests are used to query data or to change state of processes and objects. They can have or not have responses depending on their type.
All requests which interact with dedicated server accept optional parameter
timeout
. It has type float
and is measured in seconds.
In contrast with raw server's communication interfaces, requests API of Airbridge provides seamless multiplexing of requests comming from multiple clients.
The following part of documentation lists and describes REST API endpoints which are available over HTTP.
Bodies of POST requests and responses of all requests are formatted as JSON.
All endpoints accept optional pretty
query parameter. For example:
/info?pretty
. It tells endpoints to make "pretty" output by adding
indents. This can be useful for debugging.
Timeouts are passed as query parameters also, e.g.: /info?timeout=3
GET /
Check status of Airbridge and dedicated server. Can be useful for health checking and failure detection with tools like Consul.
- Parameters
- No parameters.
- Responses
200
Server is alive.
- Example
{ "status": "alive" }
- Authorization
- No authorization.
GET /info
Get information about server. Wraps
server
console command.- Parameters
- No parameters.
- Responses
200
Serialized il2fb.ds.middleware.console.structures.ServerInfo structure.
- Example
{ "type": "Local server", "name": "Development server", "description": "Dedicated Server for local tests", "__type__": "il2fb.ds.middleware.console.structures.ServerInfo" }
- Authorization
- No authorization.
GET /humans
Get list of users connected to server. Wraps
user
console command.- Parameters
- No parameters.
- Responses
200
List of il2fb.ds.middleware.console.structures.Human structures.
- Example
[ { "callsign": "john.doe", "ping": 15, "score": 0, "belligerent": { "name": "red", "value": 1, "verbose_name": "red", "help_text": null }, "aircraft": { "designation": "* Red 1", "type": "Yak-1" }, "__type__": "il2fb.ds.middleware.console.structures.Human" } ]
- Authorization
- Required if configured.
GET /humans/count
Get number of users connected to server. Equals to a number of records returned by
user
console command.- Parameters
- No parameters.
- Responses
200
Integer representing number of connected users.
- Example
7
- Authorization
- Required if configured.
GET /humans/statistics
Get server's statistics for users connected to server. Wraps
user STAT
console command.- Parameters
- No parameters.
- Responses
200
List of il2fb.ds.middleware.console.structures.HumanStatistics structures.
- Example
[ { "callsign": "john.doe", "score": 0, "state": "Landed at Airfield", "enemy_aircraft_kills": 0, "enemy_static_aircraft_kills": 0, "enemy_tank_kills": 0, "enemy_car_kills": 0, "enemy_artillery_kills": 0, "enemy_aaa_kills": 0, "enemy_wagon_kills": 0, "enemy_ship_kills": 0, "enemy_radio_kills": 0, "friendly_aircraft_kills": 0, "friendly_static_aircraft_kills": 0, "friendly_tank_kills": 0, "friendly_car_kills": 0, "friendly_artillery_kills": 0, "friendly_aaa_kills": 0, "friendly_wagon_kills": 0, "friendly_ship_kills": 0, "friendly_radio_kills": 0, "bullets_fired": 0, "bullets_hit": 0, "bullets_hit_air_targets": 0, "rockets_launched": 0, "rockets_hit": 0, "bombs_dropped": 0, "bombs_hit": 0, "__type__": "il2fb.ds.middleware.console.structures.HumanStatistics" } ]
- Authorization
- Required if configured.
POST /humans/kick
Kick all users from server.
- Responses
200
Empty dictionary.
- Example
{}
- Authorization
- Required if configured.
POST /humans/<callsign>/kick
Kick user from server by user's callsign.
- Parameters
- In URL
callsign
Callsign of user to kick.
- Type
string
- Example
/humans/john.doe/kick
- Responses
200
Empty dictionary.
- Example
{}
- Authorization
- Required if configured.
POST /chat
Send message in chat to everyone.
- Parameters
- In body
message
Message to send.
- Type
string
- Body example
{ "message": "hello!" }
- Responses
200
Empty dictionary.
- Example
{}
- Authorization
- Required if configured.
POST /chat/humans/<addressee>
Send message in chat to a user.
- Parameters
- In URL
addressee
Callsign of user to chat to.
- Type
string
- Example:
/chat/humans/john.doe
- In body
message
Message to send.
- Type
string
- Body example
{ "message": "hello!" }
- Responses
200
Empty dictionary.
- Example
{}
- Authorization
- Required if configured.
POST /chat/belligerents/<addressee>
Send message in chat to a belligerent (army).
- Parameters
- In URL
addressee
Belligerent to chat to. See il2fb.commons.organization.Belligerents for details.
- Type
integer
- Example:
/chat/belligerents/1
- In body
message
Message to send.
- Type
string
- Body example
{ "message": "hello!" }
- Responses
200
Empty dictionary.
- Example
{}
- Authorization
- Required if configured.
GET /missions/<path>
Browse missions storage (directories,
.mis
and.properties
files).- Parameters
- In URL
path
Path to a directory or mission relative to server's
Missions
directory.Missions
root directory is used ifpath
is not specified.- Type
string
- Example for directory
/missions/Net/dogfight
- Example for mission
/missions/Net/dogfight/demo_sample.mis
- In query:
json
- Optional parameter for getting parsed mission instead of raw text. Parsing is done by il2fb-mission-parser library.
- Type
string
- Example
/missions/Net/dogfight/demo_sample.mis?json
- Responses
200
List of files and directories if resource is a directory.
- Example
{ "dirs": [ " 1", " 2", " 3", " 4", "Pacific Fighters" ], "files": [ "demo_sample.mis", "demo_sample_ru.properties" ] }
200
- Mission content as plain text if resource is a mission.
200
- Parsed mission content as JSON if resource is a mission and
json
parameter is specified. Refer to parser's demo page to explore resulting format. 404
- Requested resource does not exist.
500
- Mission parsing or another error has occurred.
- Authorization
- Required if configured.
POST /missions/<path>
Upload mission and properties to a given directory in storage.
- Parameters
- In URL
path
Path to a directory relative to server's
Missions
directory.Missions
root directory is used ifpath
is not specified.- Type
string
- Example
/missions/Net/dogfight
- In body
Mission and properties are passed as parts of
multipart/form-data
request. Name of form fields does not matter. Amount of files being uploaded is not limited.- Request body example:
POST /missions/Net/dogfight/dev HTTP/1.1 Host: 127.0.0.1:5000 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="file"; filename="demo_sample.mis" Content-Type: ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="props"; filename="demo_sample_ru.properties" Content-Type: ------WebKitFormBoundary7MA4YWxkTrZu0gW--
- Responses
200
Empty dictionary.
- Example
{}
- Side effects
- Target directory is created if it does not exist.
- Files are overwritten if they are already exist.
- Authorization
- Required if configured.
DELETE /missions/<path>
Delete mission with its property files from storage.
- Parameters
- In URL
path
Path to a mission relative to server's
Missions
directory.- Type
string
- Example
/missions/Net/dogfight/demo_sample.mis
- Responses
200
Empty dictionary.
- Example
{}
404
- Requested mission does not exist.
- Side effects
.property
files which are associated with a given mission are also deleted if present.- Authorization
- Required if configured.
GET /missions/current/info
Get information about current mission. Wraps
mission
console command.- Parameters
- No parameters.
- Responses
200
Serialized il2fb.ds.middleware.console.structures.MissionInfo structure.
- Example
{ "status": { "name": "not_loaded" }, "file_path": null, "__type__": "il2fb.ds.middleware.console.structures.MissionInfo" }
- Authorization
- Required if configured.
POST /missions/<path>/load
Load a given mission to make it current. Wraps
mission LOAD
console command.- Parameters
- In URL
path
Path to a mission relative to server's
Missions
directory.- Type
string
- Example
/missions/Net/dogfight/demo_sample.mis/load
- Responses
200
Empty dictionary.
- Example
{}
- Authorization
- Required if configured.
POST /missions/current/begin
Begin current mission. Wraps
mission BEGIN
console command.- Parameters
- No parameters.
- Responses
200
Empty dictionary.
- Example
{}
- Authorization
- Required if configured.
POST /missions/current/end
End current mission. Wraps
mission END
console command.- Parameters
- No parameters.
- Responses
200
Empty dictionary.
- Example
{}
- Authorization
- Required if configured.
POST /missions/current/unload
Unload current mission. Wraps
mission DESTROY
console command.- Parameters
- No parameters.
- Responses
200
Empty dictionary.
- Example
{}
- Authorization
- Required if configured.
GET /radar/ships
Get positions of all ships (moving and stationary).
- Parameters
- No parameters.
- Responses
200
List of il2fb.ds.middleware.device_link.structures.ShipPosition structures.
- Example
[ { "index": 0, "id": "0_Chief", "pos": { "x": 8445, "y": 138394 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 1, "id": "1_Chief", "pos": { "x": 37758, "y": 225193 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 2, "id": "8_Chief", "pos": { "x": 29003, "y": 152135 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 3, "id": "70_Static", "pos": { "x": 43387, "y": 154521 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 4, "id": "72_Static", "pos": { "x": 43448, "y": 152697 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ]
- Authorization
- Required if configured.
GET /radar/ships/moving
Get positions of moving ships.
- Parameters
- No parameters.
- Responses
200
List of il2fb.ds.middleware.device_link.structures.ShipPosition structures.
- Example
[ { "index": 0, "id": "0_Chief", "pos": { "x": 8341, "y": 138642 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 1, "id": "1_Chief", "pos": { "x": 37510, "y": 224931 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 2, "id": "8_Chief", "pos": { "x": 28869, "y": 152486 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ]
- Authorization
- Required if configured.
GET /radar/ships/stationary
Get positions of stationary ships.
- Parameters
- No parameters.
- Responses
200
List of il2fb.ds.middleware.device_link.structures.ShipPosition structures.
- Example
[ { "index": 3, "id": "70_Static", "pos": { "x": 43387, "y": 154521 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 4, "id": "72_Static", "pos": { "x": 43448, "y": 152697 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ]
- Authorization
- Required if configured.
GET /radar/aircrafts/moving
Get positions of moving aircrafts (controlled by users or AI).
- Parameters
- No parameters.
- Responses
200
List of il2fb.ds.middleware.device_link.structures.MovingAircraftPosition structures.
- Example
[ { "index": 0, "id": "I_JG100", "pos": { "x": 80396, "y": 168150, "z": 1511 }, "is_human": false, "member_index": 0, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 1, "id": "I_JG100", "pos": { "x": 80329, "y": 168158, "z": 1510 }, "is_human": false, "member_index": 1, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 2, "id": "g0101", "pos": { "x": 66378, "y": 160822, "z": 1512 }, "is_human": false, "member_index": 0, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 3, "id": "g0101", "pos": { "x": 66307, "y": 160823, "z": 1510 }, "is_human": false, "member_index": 1, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 4, "id": "john.doe", "pos": { "x": 110695, "y": 202555, "z": 11 }, "is_human": true, "member_index": null, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" } ]
- Authorization
- Required if configured.
GET /radar/ground-units/moving
Get positions of moving ground units.
- Parameters
- No parameters.
- Responses
200
List of il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition structures.
- Example
[ { "index": 0, "id": "2_Chief", "member_index": 0, "pos": { "x": 99673, "y": 202473, "z": 43 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" }, { "index": 1, "id": "4_Chief", "member_index": 0, "pos": { "x": 163918, "y": 204481, "z": 15 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" }, { "index": 2, "id": "4_Chief", "member_index": 1, "pos": { "x": 163928, "y": 204471, "z": 14 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" } ]
- Authorization
- Required if configured.
GET /radar/moving
Get positions of all moving actors (aircrafts, ground units and moving ships).
- Parameters
- No parameters.
- Responses
200
Serialized structure il2fb.ds.airbridge.radar.AllMovingActorsPositions.
- Example
{ "aircrafts": [ { "index": 0, "id": "I_JG100", "pos": { "x": 82480, "y": 161721, "z": 1861 }, "is_human": false, "member_index": 0, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 1, "id": "john.doe", "pos": { "x": 110695, "y": 202554, "z": 11 }, "is_human": true, "member_index": null, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" } ], "ground_units": [ { "index": 0, "id": "2_Chief", "member_index": 0, "pos": { "x": 99903, "y": 203297, "z": 41 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" }, { "index": 1, "id": "3_Chief", "member_index": 0, "pos": { "x": 88322, "y": 184137, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" } ], "ships": [ { "index": 0, "id": "0_Chief", "pos": { "x": 7720, "y": 140132 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 1, "id": "1_Chief", "pos": { "x": 35568, "y": 222874 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ], "__type__": "il2fb.ds.airbridge.radar.AllMovingActorsPositions" }
- Authorization
- Required if configured.
GET /radar/houses
Get positions of houses.
- Parameters
- No parameters.
- Responses
200
List of il2fb.ds.middleware.device_link.structures.HousePosition structures.
- Example
[ { "index": 0, "id": "0_bld", "pos": { "x": 100184, "y": 167170 }, "status": { "name": "alive", "value": "A" }, "__type__": "il2fb.ds.middleware.device_link.structures.HousePosition" }, { "index": 1, "id": "1_bld", "pos": { "x": 100174, "y": 167142 }, "status": { "name": "alive", "value": "A" }, "__type__": "il2fb.ds.middleware.device_link.structures.HousePosition" } ]
- Authorization
- Required if configured.
GET /radar/stationary-objects
Get positions of stationary objects.
- Parameters
- No parameters.
- Responses
200
List of il2fb.ds.middleware.device_link.structures.StationaryObjectPosition structures.
- Example
[ { "index": 0, "id": "0_Static", "pos": { "x": 71906, "y": 178119, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.StationaryObjectPosition" }, { "index": 1, "id": "1_Static", "pos": { "x": 71616, "y": 176956, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.StationaryObjectPosition" } ]
- Authorization
- Required if configured.
GET /radar/stationary
Get positions of all stationary actors (stationary objects, houses and stationary ships).
- Parameters
- No parameters.
- Responses
200
Serialized structure il2fb.ds.airbridge.radar.AllStationaryActorsPositions.
- Example
{ "stationary_objects": [ { "index": 0, "id": "0_Static", "pos": { "x": 71906, "y": 178119, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.StationaryObjectPosition" }, { "index": 1, "id": "1_Static", "pos": { "x": 71616, "y": 176956, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.StationaryObjectPosition" } ], "houses": [ { "index": 0, "id": "0_bld", "pos": { "x": 100184, "y": 167170 }, "status": { "name": "alive", "value": "A" }, "__type__": "il2fb.ds.middleware.device_link.structures.HousePosition" }, { "index": 1, "id": "1_bld", "pos": { "x": 100174, "y": 167142 }, "status": { "name": "alive", "value": "A" }, "__type__": "il2fb.ds.middleware.device_link.structures.HousePosition" } ], "ships": [ { "index": 3, "id": "70_Static", "pos": { "x": 43387, "y": 154521 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 4, "id": "72_Static", "pos": { "x": 43448, "y": 152697 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ], "__type__": "il2fb.ds.airbridge.radar.AllStationaryActorsPositions" }
- Authorization
- Required if configured.
Airbridge provides requests API over NATS by using it's request-reply mechanism.
All messages are formatted as JSON just like in case of REST.
Each request message defines its operation by opcode
parameter of
integer
type.
Those requests, which accept arguments, specify payload
parameter as
dictionary.
Optional timeout
argument is also available for all requests. As in case
of REST API, this parameter has type float
and is measured in seconds, for
example:
{
"opcode": 0,
"payload": {
"timeout": 5
}
}
Every response contains status
. It is an integer representation of request
execution status, where 0
stands for success and 1
— for failure.
Example:
{
"status": 0
}
Available NATS requests are listed below along with examples of responses.
GET_SERVER_INFO
Get information about server. Wraps
server
console command.- Opcode
0
- Parameters
- No parameters.
- Request example
{ "opcode": 0 }
- Response example:
{ "status": 0, "payload": { "type": "Local server", "name": "Development server", "description": "Dedicated Server for local tests", "__type__": "il2fb.ds.middleware.console.structures.ServerInfo" } }
GET_HUMANS_LIST
Get list of users connected to server. Wraps
user
console command.- Opcode
10
- Parameters
- No parameters.
- Request example
{ "opcode": 10 }
- Response example:
{ "status": 0, "payload": [ { "callsign": "john.doe", "ping": 61, "score": 0, "belligerent": { "name": "none", "value": 0, "verbose_name": "none", "help_text": null, }, "aircraft": null, "__type__": "il2fb.ds.middleware.console.structures.Human" } ] }
GET_HUMANS_COUNT
Get number of users connected to server. Equals to a number of records returned by
user
console command.- Opcode
11
- Parameters
- No parameters.
- Request example
{ "opcode": 11 }
- Response example:
{ "status": 0, "payload": 7 }
GET_HUMANS_STATISTICS
Get server's statistics for users connected to server. Wraps
user STAT
console command.- Opcode
12
- Parameters
- No parameters.
- Request example
{ "opcode": 12 }
- Response example:
{ "status": 0, "payload": [ { "callsign": "john.doe", "score": 0, "state": "Selects Aircraft", "enemy_aircraft_kills": 0, "enemy_static_aircraft_kills": 0, "enemy_tank_kills": 0, "enemy_car_kills": 0, "enemy_artillery_kills": 0, "enemy_aaa_kills": 0, "enemy_wagon_kills": 0, "enemy_ship_kills": 0, "enemy_radio_kills": 0, "friendly_aircraft_kills": 0, "friendly_static_aircraft_kills": 0, "friendly_tank_kills": 0, "friendly_car_kills": 0, "friendly_artillery_kills": 0, "friendly_aaa_kills": 0, "friendly_wagon_kills": 0, "friendly_ship_kills": 0, "friendly_radio_kills": 0, "bullets_fired": 0, "bullets_hit": 0, "bullets_hit_air_targets": 0, "rockets_launched": 0, "rockets_hit": 0, "bombs_dropped": 0, "bombs_hit": 0, "__type__": "il2fb.ds.middleware.console.structures.HumanStatistics" } ] }
KICK_ALL_HUMANS
Kick all users from server.
- Opcode
20
- Parameters
- No parameters.
- Request example
{ "opcode": 20 }
- Response example:
{ "status": 0, "payload": 0 }
KICK_HUMAN_BY_CALLSIGN
Kick user from server by user's callsign.
- Opcode
21
- Parameters
callsign
Callsign of user to kick.
- Type
string
- Request example
{ "opcode": 21, "payload": { "callsign": "john.doe" } }
- Response example:
{ "status": 0, "payload": null }
CHAT_TO_ALL
Send message in chat to everyone.
- Opcode
30
- Parameters
message
Message to send.
- Type
string
- Request example
{ "opcode": 30, "payload": { "message": "hello!" } }
- Response example:
{ "status": 0, "payload": null }
CHAT_TO_HUMAN
Send message in chat to a user.
- Opcode
31
- Parameters
message
Message to send.
- Type
string
addressee
Callsign of user to chat to.
- Type
string
- Request example
{ "opcode": 31, "payload": { "message": "hello!", "addressee": "john.doe" } }
- Response example:
{ "status": 0, "payload": null }
CHAT_TO_BELLIGERENT
Send message in chat to a belligerent (army).
- Opcode
32
- Parameters
message
Message to send.
- Type
string
addressee
Callsign of belligerent to chat to. See il2fb.commons.organization.Belligerents for details.
- Type
integer
- Request example
{ "opcode": 32, "payload": { "message": "hello!", "addressee": 1 } }
- Response example:
{ "status": 0, "payload": null }
GET_MISSION_INFO
Get information about current mission. Wraps
mission
console command.- Opcode
40
- Parameters
- No parameters.
- Request example
{ "opcode": 40 }
- Response example:
{ "status": 0, "payload": { "status": { "name": "not_loaded" }, "file_path": null, "__type__": "il2fb.ds.middleware.console.structures.MissionInfo" } }
LOAD_MISSION
Load a given mission to make it current. Wraps
mission LOAD
console command.- Opcode
41
- Parameters
file_path
Path to a mission relative to server's
Missions
directory.- Type
string
- Request example
{ "opcode": 41, "payload": { "file_path": "Net/dogfight/demo_sample.mis" } }
- Response example:
{ "status": 0, "payload": null }
BEGIN_MISSION
Begin current mission. Wraps
mission BEGIN
console command.- Opcode
42
- Parameters
- No parameters.
- Request example
{ "opcode": 42 }
- Response example:
{ "status": 0, "payload": null }
END_MISSION
End current mission. Wraps
mission END
console command.- Opcode
43
- Parameters
- No parameters.
- Request example
{ "opcode": 43 }
- Response example:
{ "status": 0, "payload": null }
UNLOAD_MISSION
Unload current mission. Wraps
mission DESTROY
console command.- Opcode
44
- Parameters
- No parameters.
- Request example
{ "opcode": 44 }
- Response example:
{ "status": 0, "payload": null }
GET_ALL_SHIPS_POSITIONS
Get positions of all ships (moving and stationary).
- Opcode
50
- Parameters
- No parameters.
- Request example
{ "opcode": 50 }
- Response example:
{ "status": 0, "payload": [ { "index": 0, "id": "0_Chief", "pos": { "x": 8445, "y": 138394 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 1, "id": "1_Chief", "pos": { "x": 37758, "y": 225193 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 2, "id": "8_Chief", "pos": { "x": 29003, "y": 152135 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 3, "id": "70_Static", "pos": { "x": 43387, "y": 154521 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 4, "id": "72_Static", "pos": { "x": 43448, "y": 152697 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ] }
GET_MOVING_SHIPS_POSITIONS
Get positions of moving ships.
- Opcode
51
- Parameters
- No parameters.
- Request example
{ "opcode": 51 }
- Response example:
{ "status": 0, "payload": [ { "index": 0, "id": "0_Chief", "pos": { "x": 8445, "y": 138394 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 1, "id": "1_Chief", "pos": { "x": 37758, "y": 225193 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 2, "id": "8_Chief", "pos": { "x": 29003, "y": 152135 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ] }
GET_STATIONARY_SHIPS_POSITIONS
Get positions of stationary ships.
- Opcode
52
- Parameters
- No parameters.
- Request example
{ "opcode": 52 }
- Response example:
{ "status": 0, "payload": [ { "index": 3, "id": "70_Static", "pos": { "x": 43387, "y": 154521 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 4, "id": "72_Static", "pos": { "x": 43448, "y": 152697 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ] }
GET_MOVING_AIRCRAFTS_POSITIONS
Get positions of moving aircrafts (controlled by users or AI).
- Opcode
53
- Parameters
- No parameters.
- Request example
{ "opcode": 53 }
- Response example:
{ "status": 0, "payload": [ { "index": 0, "id": "I_JG100", "pos": { "x": 80396, "y": 168150, "z": 1511 }, "is_human": false, "member_index": 0, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 1, "id": "I_JG100", "pos": { "x": 80329, "y": 168158, "z": 1510 }, "is_human": false, "member_index": 1, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 2, "id": "g0101", "pos": { "x": 66378, "y": 160822, "z": 1512 }, "is_human": false, "member_index": 0, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 3, "id": "g0101", "pos": { "x": 66307, "y": 160823, "z": 1510 }, "is_human": false, "member_index": 1, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 4, "id": "john.doe", "pos": { "x": 110695, "y": 202555, "z": 11 }, "is_human": true, "member_index": null, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" } ] }
GET_MOVING_GROUND_UNITS_POSITIONS
Get positions of moving ground units.
- Opcode
54
- Parameters
- No parameters.
- Request example
{ "opcode": 54 }
- Response example:
{ "status": 0, "payload": [ { "index": 0, "id": "2_Chief", "member_index": 0, "pos": { "x": 99673, "y": 202473, "z": 43 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" }, { "index": 1, "id": "4_Chief", "member_index": 0, "pos": { "x": 163918, "y": 204481, "z": 15 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" }, { "index": 2, "id": "4_Chief", "member_index": 1, "pos": { "x": 163928, "y": 204471, "z": 14 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" } ] }
GET_ALL_MOVING_ACTORS_POSITIONS
Get positions of all moving actors (aircrafts, ground units and moving ships).
- Opcode
55
- Parameters
- No parameters.
- Request example
{ "opcode": 55 }
- Response example:
{ "status": 0, "payload": { "aircrafts": [ { "index": 0, "id": "I_JG100", "pos": { "x": 82480, "y": 161721, "z": 1861 }, "is_human": false, "member_index": 0, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" }, { "index": 1, "id": "john.doe", "pos": { "x": 110695, "y": 202554, "z": 11 }, "is_human": true, "member_index": null, "__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition" } ], "ground_units": [ { "index": 0, "id": "2_Chief", "member_index": 0, "pos": { "x": 99903, "y": 203297, "z": 41 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" }, { "index": 1, "id": "3_Chief", "member_index": 0, "pos": { "x": 88322, "y": 184137, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition" } ], "ships": [ { "index": 0, "id": "0_Chief", "pos": { "x": 7720, "y": 140132 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 1, "id": "1_Chief", "pos": { "x": 35568, "y": 222874 }, "is_stationary": false, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ], "__type__": "il2fb.ds.airbridge.radar.AllMovingActorsPositions" } }
GET_ALL_HOUSES_POSITIONS
Get positions of houses.
- Opcode
56
- Parameters
- No parameters.
- Request example
{ "opcode": 56 }
- Response example:
{ "status": 0, "payload": [ { "index": 0, "id": "0_bld", "pos": { "x": 100184, "y": 167170 }, "status": { "name": "alive", "value": "A" }, "__type__": "il2fb.ds.middleware.device_link.structures.HousePosition" }, { "index": 1, "id": "1_bld", "pos": { "x": 100174, "y": 167142 }, "status": { "name": "alive", "value": "A" }, "__type__": "il2fb.ds.middleware.device_link.structures.HousePosition" } ] }
GET_STATIONARY_OBJECTS_POSITIONS
Get positions of stationary objects.
- Opcode
57
- Parameters
- No parameters.
- Request example
{ "opcode": 57 }
- Response example:
{ "status": 0, "payload": [ { "index": 0, "id": "0_Static", "pos": { "x": 71906, "y": 178119, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.StationaryObjectPosition" }, { "index": 1, "id": "1_Static", "pos": { "x": 71616, "y": 176956, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.StationaryObjectPosition" } ] }
GET_ALL_STATIONARY_ACTORS_POSITIONS
Get positions of all stationary actors (stationary objects, houses and stationary ships).
- Opcode
58
- Parameters
- No parameters.
- Request example
{ "opcode": 58 }
- Response example:
{ "status": 0, "payload": { "stationary_objects": [ { "index": 0, "id": "0_Static", "pos": { "x": 71906, "y": 178119, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.StationaryObjectPosition" }, { "index": 1, "id": "1_Static", "pos": { "x": 71616, "y": 176956, "z": 1 }, "__type__": "il2fb.ds.middleware.device_link.structures.StationaryObjectPosition" } ], "houses": [ { "index": 0, "id": "0_bld", "pos": { "x": 100184, "y": 167170 }, "status": { "name": "alive", "value": "A" }, "__type__": "il2fb.ds.middleware.device_link.structures.HousePosition" }, { "index": 1, "id": "1_bld", "pos": { "x": 100174, "y": 167142 }, "status": { "name": "alive", "value": "A" }, "__type__": "il2fb.ds.middleware.device_link.structures.HousePosition" } ], "ships": [ { "index": 3, "id": "70_Static", "pos": { "x": 43387, "y": 154521 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" }, { "index": 4, "id": "72_Static", "pos": { "x": 43448, "y": 152697 }, "is_stationary": true, "__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition" } ], "__type__": "il2fb.ds.airbridge.radar.AllStationaryActorsPositions" } }
As it was stated earlier, Airbridge provides multiple streaming facilities. This means that it's possible to subscribe to a stream of events which originate from different sources. The following sources are provided:
chat
— messages coming from chat. This includes messages from server and system.events
— events coming from game log and user-connection events coming from server's console;not parsed strings
— strings coming from game log which were not parsed due some error;radar
— coordinates of all moving actors which are queried periodically and period is specified for each subscriber separatelly. Default refresh period is5 sec
.
Streaming facilities allow subscription of any object which conforms to StreamingSubscriber interface.
Those subscribers which conform to PluggableStreamingSubscriber interface, can be created automatically at startup of application. TextFileStreamingSink, JSONFileStreamingSink and NATSStreamingSink are examples of pluggable subscribers. Configuration of such subscribers is explained in "Configuration" section.
All streaming data is transmitted as message which are formatted as JSON
strings. Each message contains a timestamp
which indicates time when event
was detected and data
which contains event-related data.
NOTE: event's timestamp indicates time when event was detected, not
the time when it has occured. Usually these times are equal, but there may
be a slight difference, for example, for game log events: game log is
monitored by polling file with a specific period and events may occur
before log watcher will notice them. Moreover, game server may write
messages to game log with delay. So, it's better to extract event's time
from event's data if it is present and to use timestamp
field as event
identifier.
Examples of messages from different streaming facilities are given below.
Message from chat
stream:
{
"timestamp": "2017-11-25T13:22:42.145599",
"data": {
"body": "john.doe joins the game.",
"actor": null,
"from_human": false,
"from_server": false,
"from_system": true,
"__type__": "il2fb.ds.middleware.console.events.ChatMessageWasReceived"
}
}
Message from events
stream:
{
"timestamp": "2017-11-25T15:22:45.211668",
"data": {
"time": "15:22:44",
"actor": {
"flight": "g0101",
"aircraft": 3
},
"pos": {
"x": 55079.348,
"y": 175689.23
},
"__type__": "il2fb.parsers.game_log.events.AIAircraftHasDespawned"
}
}
Message from not parsed strings
stream:
{
"timestamp": "2017-11-25T15:19:33.754441",
"data": {
"value": "[3:19:33 PM] 3do/Tree/Line/live.sim destroyed by 8_Chief at 69716.7 158365.38",
"__type__": "il2fb.ds.airbridge.dedicated_server.game_log.NotParsedGameLogString"
}
}
Message from radar
stream:
{
"timestamp": "2017-11-25T15:50:51.689771",
"data": {
"aircrafts": [
{
"index": 0,
"id": "I_JG100",
"pos": {
"x": 82480,
"y": 161721,
"z": 1861
},
"is_human": false,
"member_index": 0,
"__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition"
},
{
"index": 1,
"id": "john.doe",
"pos": {
"x": 110695,
"y": 202554,
"z": 11
},
"is_human": true,
"member_index": null,
"__type__": "il2fb.ds.middleware.device_link.structures.MovingAircraftPosition"
}
],
"ground_units": [
{
"index": 0,
"id": "2_Chief",
"member_index": 0,
"pos": {
"x": 99903,
"y": 203297,
"z": 41
},
"__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition"
},
{
"index": 1,
"id": "3_Chief",
"member_index": 0,
"pos": {
"x": 88322,
"y": 184137,
"z": 1
},
"__type__": "il2fb.ds.middleware.device_link.structures.MovingGroundUnitPosition"
}
],
"ships": [
{
"index": 0,
"id": "0_Chief",
"pos": {
"x": 7720,
"y": 140132
},
"is_stationary": false,
"__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition"
},
{
"index": 1,
"id": "1_Chief",
"pos": {
"x": 35568,
"y": 222874
},
"is_stationary": false,
"__type__": "il2fb.ds.middleware.device_link.structures.ShipPosition"
}
],
"__type__": "il2fb.ds.airbridge.radar.AllMovingActorsPositions"
}
}
The subsections below describe different subscribers which can be used as streaming destination.
Airbridge supports streaming of data to local files. In this case every single line in text file will contain a message serialized as a single JSON string.
This is the simplest and the fastest streaming subscriber, however it is limited to local file system of server.
Events from different streaming facilities must go to different output files.
Streaming to files can be configured to run from start of application.
Refer to "Configuration" section for examples and details.
Streaming to NATS channels allows Airbridge to send data to remote storage.
This is one of the key functionalities of Airbridge, as it allows to escape server's file system and operating system at all. This also makes it possible for multiple remote consumers to subscribe to events in different combinations.
Also NATS streaming server allows to configure persistence of messages, so they can be accessed and processed in future.
Each streaming facility can publish messages to its own channel (subject). Publishing all messages to a single channel is also possible if needed.
Streaming to NATS channels can be configured to run from start of application.
Refer to "Configuration" section for examples and details.
Airbridge allows its clients to subscribe to streaming facilities via WebSockets.
This means that web application can show data in real time in browser. Such feature can be used for building admin dashboards for Airbridge. It's not recommended to use this API for displaying data to end users in production, as this can affect overall performance of Airbridge.
To start any subscription, a client must connect to streaming endpoint via
web-socket. This is done by sending HTTP GET
request to /streaming
route, e.g.:
GET ws://127.0.0.1:5000/streaming
After connection is established, the client can send messages to server to subscribe to or unsubscribe from a specific streaming facility.
Like in case of NATS requests API, each request to WS streaming subscription
API is specified by operation code opcode
. Responses have similar structure
as well: every response contains integer status
field, where 0
stands
for success and 1
— for failure.
Subscription requests are described below.
SUBSCRIBE_TO_CHAT
Subscribe to
chat
stream.- Opcode
0
- Parameters
- No parameters.
- Request example
{ "opcode": 0 }
- Response example:
{ "status": 0 }
UNSUBSCRIBE_FROM_CHAT
Unsubscribe from
chat
stream.- Opcode
1
- Parameters
- No parameters.
- Request example
{ "opcode": 1 }
- Response example:
{ "status": 0 }
SUBSCRIBE_TO_EVENTS
Subscribe to
events
stream.- Opcode
10
- Parameters
- No parameters.
- Request example
{ "opcode": 10 }
- Response example:
{ "status": 0 }
UNSUBSCRIBE_FROM_EVENTS
Unsubscribe from
events
stream.- Opcode
11
- Parameters
- No parameters.
- Request example
{ "opcode": 11 }
- Response example:
{ "status": 0 }
SUBSCRIBE_TO_NOT_PARSED_STRINGS
Subscribe to
not parsed strings
stream.- Opcode
20
- Parameters
- No parameters.
- Request example
{ "opcode": 20 }
- Response example:
{ "status": 0 }
UNSUBSCRIBE_FROM_NOT_PARSED_STRINGS
Unsubscribe from
not parsed strings
stream.- Opcode
21
- Parameters
- No parameters.
- Request example
{ "opcode": 21 }
- Response example:
{ "status": 0 }
SUBSCRIBE_TO_RADAR
Subscribe to
radar
stream.- Opcode
30
- Parameters
refresh_period
Refresh period of radar for current subscriber. Measured in seconds. The parameter is optional.
- Type
float
- Request example
{ "opcode": 30, "payload": { "refresh_period": 30 } }
- Response example:
{ "status": 0 }
UNSUBSCRIBE_FROM_RADAR
Unsubscribe from
radar
stream.- Opcode
31
- Parameters
- No parameters.
- Request example
{ "opcode": 31 }
- Response example:
{ "status": 0 }
Information about project's releases can be found at releases page.
Each release includes release notes, precompiled binaries and sources.
This section describes possible ways to install Airbridge application. The easiest way is to install from binary which is described below.
Airbridge comes with precompiled executable binaries which are available at releases page (see the section above). Installation is simple and it is done just by unpacking executable file from release archive which is suitable for target operating system.
It's also possible to get Airbridge as Python package from PyPI
(Python Package Index). It is available as il2fb-ds-airbridge
package and can be installed via pip
:
pip install il2fb-ds-airbridge
Same via easy_install
:
easy_install il2fb-ds-airbridge
NOTE: Airbridge is implemented using Python 3.6, so at least this version must be used to run the application.
If neither precompiled version nor package are suitable or debugging/development is needed, then Airbridge can be installed from local sources.
Sources can be obtained by cloning Git repository or by downloading them from releases page.
Usual installation can be done by executing setup script:
python ./setup.py install
It is also possible to install application as editable package, so that changes in source code will be applied immediately:
pip install -e .
To compile binary from source one will need to use PyInstaller.
Its spec
file is defined as airbridge.spec
at the root of source
directory. This makes compilation to be very simple:
pyinstaller airbridge.spec -y --clean
PyInstaller will create a binary executable inside dist
directory.
NOTE: all dependencies must be installed locally to make it possible to compile a single binary file. Dependencies for Windows are defined atrequirements/dist-windows.txt
and dependencies for other platforms are defined atrequirements/dist.txt
.
This section describes how Airbridge can be configured.
Airbridge application requires a configuration file to operate. This requirement comes out of application's nature: it is a wrapper of dedicated server, so it needs to know at least were server's executable file is located.
Application's configuration has hierarchical structure and is stored as a YAML file. The following subsections describe different aspects of configuration.
Full example of configuration file can be found in examples directory.
Logging is critical for detection and localization of issues and errors. As errors can occur at any stage of application execution, it is important to configure it in the first place.
Airbridge produces 2 log files: a main log file which records application's execution flow and a separate file for dumping tracebacks of exceptions.
Let's take a look at configuration of logging which is used by default:
logging:
files:
main:
level: debug
file_path: airbridge.log
keep_after_restart: yes
is_delayed: no
exceptions:
file_path: airbridge.exc
keep_after_restart: yes
is_delayed: no
rotation:
is_enabled: yes
max_size: 10485760
max_backups: 10
trace: no
encoding: utf8
use_local_time: no
files
section defines options for two log files. The options are the same
for both of them, except level
option which can be specified only for
main
file. Description of all options is given below.
level
Logging level for
main
file. Can be one of:debug
,info
,warning
,error
orcritical
.Logging level for
exceptions
file is always set todebug
, so that tracebacks from any level can be captured. Usually tracebacks are logged with log message oferror
level, however they are not limited to it. For example, warning messages also can include tracebacks.file_path
- Path to a file where log will be stored.
keep_after_restart
- Tells whether existing log file should be retained after restart of application or a clean one should be created.
is_delayed
- Tells whether file should be created only after a first message is written to it or it should be created immediately after start of application.
Rotation of log files allows to keep their size under acceptable limit. After file size reaches this limit it is backed up and new empty log file is created.
By default rotation is enabled and size limit for a single file is set to 10'485'760 bytes (10 MiB). This feature can be disabled and external tools like logrotate can be used instead.
is_enabled
- Tells whether rotation is turned on.
max_size
- Size limit for a single file.
max_backups
- Number of backups which are stored in file system before application will
start to delete old backups. For example, if
max_backups
is set to 10 and there are already 10 backups exist, then when file size of log reaches itsmax_size
limit, the oldest existing backup will be erased and a new one will be created.
Other available logging options are listed below.
trace
- Tells whether tracing level of logging is enabled. Tracing messages are
logged with
debug
level usually (but not limited to it), so it must be set as value forlevel
option for main log file. encoding
- Encoding of log files to use.
use_local_time
- Tells whether local timezone or UTC should be used in log messages.
Airbridge is designed with ability of its components to store their internal state so that it can be restored back during next run. For example, monitor of game log needs to know where it was stopped so that monitoring can be resumed from the right place to avoid duplication or omission of game events.
State is stored as a file in YAML format, so it can be easily inspected by humans. Its location is configurable and default configuration is presented below.
state:
file_path: airbridge.state
Current configuration is very simple and its options are described below.
file_path
- Path to a file where application's state will be stored.
This section decribes config options which are used to locate and run an instance of dedicated server.
Default configuration looks as following:
ds:
exe_path: C:\\il2ds\il2server.exe
config_path: C:\\il2ds\confs.ini
start_script_path: C:\\il2ds\server.cmd
wine_bin_path: wine
console_proxy:
bind:
address: localhost
port: 20001
device_link_proxy:
bind:
address: localhost
port: 10001
is_interactive: yes
Description of options is given below.
exe_path
- Path to
il2server.exe
executable file. config_path
- Optional path to server's config. By default it is
confs.ini
which is located at server's root directory. start_script_path
- Optional path to server's start script. By default it is
server.cmd
which is located at server's root directory. wine_bin_path
Custom path to Wine executable. Applicable only when running server on Linux or macOS.
NOTE: on macOS
wine
executable can be just a shell script which wraps invocation of real executable. For example/usr/local/bin/wine
can be just a wrapper around
/usr/local/Cellar/wine/1.6.2/bin/wine.bin
In such case the latter one must be used as value of
wine_bin_path
.
As it was told earlier, console proxy allows existing applications to communicate with dedicated server using their existing implementations of console clients.
By default it is turned off.
console_proxy.bind.address
- Address for console proxy to listen for incoming connections on.
console_proxy.bind.port
- Port for console proxy to listen for incoming connections on.
Just like in case of console proxy, Device Link allows existing applications to communicate with dedicated server using their existing implementations of Device Link clients.
NOTE: Despite Device Link works on top of UDP and dedicated server is able to handle requests from multiple clients, it's strongly recommended for them to use proxy, as proxy allows multiplexing of requests and controls their execution flow.
By default Device Link proxy is turned off.
device_link_proxy.bind.address
- Address for Device Link proxy to listen for incoming connections on.
device_link_proxy.bind.port
- Port for Device Link proxy to listen for incoming connections on.
By default Airbridge connects its terminal channels (STDIN, STDOUT and STDERR) to terminal channels channels of dedicated server as it is shown in "Architecture Overview" section. Such approach allows Airbridge to sit in the middle of user-to-server communication and filter data.
If application is going to be run as a background service or inside a
virtualization container like Docker and interactive communication with server
is not needed then it can be turned off by setting is_interactive
option to
no
value.
is_interactive
- Tells whether Airbridge will be running with ability of user to interact with server via its shell.
As NATS can be used for both API and streaming, it has own configuration section.
By default NATS API and streaming are turned off, so this section should be configured only if at least one of them is going to be used.
Full example of configuration:
nats:
servers:
- nats://your.domain:4222
streaming:
cluster_id: your-cluster-id
client_id: your-client-id
tls:
private_key_path: /path/to/nats/tls/client.key
certificate_path: /path/to/nats/tls/client.crt
ca_path: /path/to/nats/tls/ca
Description of shown options is given below.
servers
List of server addresses to connect to. See NATS client's documentation for details.
Required if either NATS API or streaming is going to be used.
streaming.cluster_id
ID of cluster to connect to. Cluster ID is defined at NATS server side. See streaming server's documentation for details.
Required only if NATS streaming is going to be used.
streaming.client_id
Unique client ID for a given cluster. It is defined by Airbridge user usually. See streaming server's documentation for details.
Required only if NATS streaming is going to be used.
tls.private_key_path
- Path to TLS client private key.
tls.certificate_path
- Path to TLS client certificate.
tls.ca_path
- Path to TLS client certificate CA.
API section is used to configure NATS and HTTP APIs. By default APIs are turned off.
Full example of configuration looks as following:
api:
nats:
subject: airbridge-cmd
http:
bind:
address: localhost
port: 5000
auth:
token_header_name: X-Airbridge-Token
token_storage_path: airbridge.tokens
cors:
"your.trusted.domain":
expose_headers:
- X-Custom-Server-Header
allow_headers:
- X-Requested-With
- Content-Type
max_age: 600
It's enough to configure nats.servers
and api.nats.subject
to enable
NATS API. This will tell Airbridge to subscribe to a given subject on a given
set of servers to listen for incoming requests.
HTTP API includes both REST API and streaming via WebSockets. This subsection describes configuration options for them.
Minimal configuration requires http.bind.port
option to be specified to
enable HTTP API.
Airbridge must be bound to a specific network location to allow clients to connect to it.
http.bind.address
Address to listen for incoming HTTP requests on.
Default value is
localhost
.http.bind.port
- Post to listen for incoming HTTP requests on.
It's possibe to enable authorization for HTTP requests. This is done by requiring client to provide an API token which is known only to server and client. Tokens are passed to Airbridge via HTTP headers.
API tokens are just strings which are encoded with base64 algorithm. They can contain any information and it's OK to use random data. Decision on length of tokens is up to server administrator.
Encoding to base64
can be done, for example, by
base64 utility, or by running
openssl enc -base64
command, or by using Python's
base64.b64encode()
function.
It's possible to generate random and encoded token just by using output from
/dev/urandom
device with base64
utility. For example:
cat /dev/urandom | head -c 48 | base64
This will produce a random encoded token with length of 48 characters.
Options below describe how to configure authorization via tokens.
http.auth.token_header_name
Name of HTTP header to look for token at.
Default value is
X-Airbridge-Token
.http.auth.token_storage_path
- Path to a text file with allowed tokens. Each line represents a single token. Multiple tokens can be allowed.
Cross Origin Resource Sharing can be enabled for HTTP API if needed.
Implementation is provided by aiohttp-cors library.
Options from http.cors
config section are passed to that library as-is.
Please, refer to library's documentation
for more information.
It's possible to configure static streaming subscribers for each streaming facility separately. By default no subscribers are defined.
All streaming facilities expect subscribers
to be defined for them. Some
facilities like radar
may allow definition of extra options for the whole
facility (e.g., request_timeout
).
subscribers
option defines a dictionary of subscribers of different types.
Each type of subscribers can accept its own set of arguments for
initialization. This includes, for example, path to output file or name of
a NATS channel. Such initialization options are defined by args
dictionary
which is specific for each subscriber.
Additionally, each subscriber can define additional subscription options for
different facilities. For example, subscribers of radar
facility may
specify custom refresh_period
. Such options are defined via
subscription_options
parameter.
The configuration example below shows all options which can be used to configure streaming subscribers.
NOTE: it is not necessary to define all kinds of subscribers: it may be enough to define only few of them depending on the needs. Other definitions can be found in examples directory.
streaming:
chat:
subscribers:
file:
args:
path: streaming/chat.log
nats:
args:
subject: chat
events:
subscribers:
file:
args:
path: streaming/events.log
nats:
args:
subject: events
not_parsed_strings:
subscribers:
file:
args:
path: streaming/not_parsed_strings.log
nats:
args:
subject: not-parsed-strings
radar:
request_timeout: 5
subscribers:
file:
args:
path: streaming/radar.log
subscription_options:
refresh_period: 5
nats:
args:
subject: radar
subscription_options:
refresh_period: 5
Description of subscriber types with their initialization arguments is given below.
file
File subscriber which puts messages to JSON text file, 1 line per single message.
Args:
path
- Path to output file.
nats
NATS subscriber which publishes messages to NATS subject (channel).
Args:
subject
- Name of NATS subject to publish messages to.
chat
, events
and not_parsed_strings
facilities are similar from
configurational point of view and do not have extra options.
On the other hand, radar
facility accepts request_timeout
option which
sets timeout in seconds for Device Link requests. By default there is no
timeout. Additionally, radar
allows to set custom refresh_period
in
seconds for each subscriber via subscription_options
parameter.
This section is covering resolution of possible security pitfalls which might arise while using Airbridge and bare Dedicated Server.
It's worth noting that Dedicated Server must be secured no matter of what: whether it runs with or without Airbridge, under control of other software or in a stand-alone mode. As Dedicated Server exposes console and Device Link to the outer world, access to them must be restricted.
So, two techniques must be applied. First of all, list of allowed hosts must
be explicitly set for both console and Device Link. This is done by setting
IPS
value for [Console]
and [DeviceLink]
sections of server's config file. Preferred value is 127.0.0.1
or
localhost
. This with ensure that connections coming from other machines
will be blocked.
Next, it's good to be sure that unwanted connections will be not only blocked but impossible at all. This can be achieved by explicit binding of connection listeners to localhost.
As for Device Link, this can be easily done by setting localhost
as value
for host
option in [DeviceLink]
section of server's config.
As for console, this cannot be done, as console is bound to the same address which is used for incoming connections from players.
Configuration of dedicated server may be tricky and misleading sometimes, so
it's recommended to use config editor
for this purpose. Visit Connection
, Console
and Device link
tabs
to edit described values.
Airbridge exposes control over dedicated server to the outer world by design purposely, so extra security care must be taken of it. Access must be as scrict as possibe.
Sensitive posints:
- HTTP API.
- Connection with NATS: from server to NATS and from NATS to clients.
Both HTTP and NATS API provide interface for managing server's state and for
message subscription. However, HTTP API provides access to server's mission
storage which is a usual directory is server's file system. So, control over
files in Mission
directory is exposed also.
Access to HTTP API can be resctricted by using access tokens, as it is described in configuration section. However, implementation is rather naïve and it has a lot of place for imrovements. Nevertheless, Airbridge is designed mainly as an interlayer between dedicated servers and server commanders, so its API is not expected to be exposed to public networks.
Another thing which is strongly recommended to do is to allow access to Airbridge via HTTPS only. This is beyond scope of this documentation. For example, HTTP API can be bound to local host and requests to it can be proxied by Nginx with configured support of TLS. This is the most preferred approach.
NATS server is expected to be remote in respect to dedicated server. It has its own optional authorization mechanisms and it can run over TLS. Refer to security documentation for NATS streaming server and security documentation for bare NATS server.
Enabling TLS for server with requirement for its clients to use TLS and enabling validation of clients' certificates would act as a pretty good solution.
This section describes ways to use Airbridge and preconditions which must be satisfied before actual usage.
As Airbridge communicates with dedicated server via console and Device Link interfaces, they must be enabled for server and connections from localhost must be allowed. Feel free to do this with config editor.
In case Airbridge is going to be used on Linux or macOS, Wine must be installed, so that dedicated server can be run.
There are a couple of ways to run Airbridge depending on how it was installed.
If it was installed as a Python package, then it can be run as a command-line application:
il2fb-ds-airbridge
If it was installed as a single executable, then it can be run just by invoking it:
airbridge
On Windows it can be run both from console and as a usual application.
Airbridge accepts optional path to its config file. By default it looks for
config to be in airbridge.yml
file in current working directory.
Example of application's help is given below.
il2fb-ds-airbridge -h usage: il2fb-ds-airbridge [-h] [-c CONFIG_PATH] Wrapper of dedicated server of «IL-2 Sturmovik: Forgotten Battles» optional arguments: -h, --help show this help message and exit -c CONFIG_PATH, --config CONFIG_PATH path to config file (default: airbridge.yml)
Current section accumulates information about different potential issues which were already described in this text just to give an extra emphasis on them.
- Console and Device Link interfaces must be enabled for dedicated server.
- Access to Console and Device Link interfaces should be granted to localhost only.
wine
must be installed to run dedicated server on Linux or macOS.- Path to
wine.bin
must be configured bywine_bin_path
when running on macOS. - Config file must be create before runnsing Airbridge.
- Access to HTTP API should be granted only to authorized clients.
- If HTTP API is exposed to the outer world if must be secured and run over HTTPS.
- Connection with NATS server must be secured with TLS or at least isolated.
- Does Airbridge impact performance of Dedicated Server?
No, Airbridge is a stand-alone process which runs Dedicated Server as a coprocess and does not consume it's resources.
- Can Airbridge extend server's functionality or fix its bugs?
No, Airbridge just wraps access to existing interfaces and provides unified machine-friendly access to them.
- On which operating systems Airbridge can run?
Airbridge was tested on Windows (7, 10, Server 2008), Linux and macOS.