Initial Commit
This commit is contained in:
20
crates/dcs-grpc/Cargo.toml
Normal file
20
crates/dcs-grpc/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "dcs-grpc"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bevy.workspace = true
|
||||
tonic = "0.10"
|
||||
prost = "0.12"
|
||||
prost-types = "0.12"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.10.2"
|
||||
protoc-bundled = { git = "https://github.com/rkusa/protoc-bundled.git", rev = "3.21.6" }
|
||||
82
crates/dcs-grpc/build.rs
Normal file
82
crates/dcs-grpc/build.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
std::env::set_var("PROTOC", protoc_bundled::PROTOC);
|
||||
std::env::set_var("PROTOC_INCLUDE", protoc_bundled::PROTOC_INCLUDE);
|
||||
|
||||
println!("cargo:rerun-if-changed=migrations");
|
||||
println!("cargo:rerun-if-changed=protos/dcs");
|
||||
|
||||
build_dcs_grpc_db()?;
|
||||
|
||||
// build_commands()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn build_commands() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
fn build_dcs_grpc_db() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tonic_build::configure()
|
||||
.type_attribute(".", "#[derive(::serde::Serialize, ::serde::Deserialize)]")
|
||||
.type_attribute(".", "#[serde(rename_all = \"camelCase\")]")
|
||||
.type_attribute(
|
||||
"dcs.mission.v0.StreamEventsResponse.event",
|
||||
"#[serde(tag = \"type\")]",
|
||||
)
|
||||
.type_attribute(
|
||||
"dcs.common.v0.Unit",
|
||||
"#[serde(from = \"UnitIntermediate\")]",
|
||||
)
|
||||
.type_attribute(
|
||||
"dcs.common.v0.Weapon",
|
||||
"#[serde(from = \"WeaponIntermediate\")]",
|
||||
)
|
||||
.type_attribute(
|
||||
"dcs.unit.v0.GetTransformResponse",
|
||||
"#[serde(from = \"GetTransformResponseIntermediate\")]",
|
||||
)
|
||||
.type_attribute(
|
||||
"dcs.mission.v0.StreamUnitsResponse.update",
|
||||
"#[allow(clippy::large_enum_variant)]",
|
||||
)
|
||||
.field_attribute(
|
||||
"dcs.mission.v0.StreamEventsResponse.MarkAddEvent.visibility",
|
||||
"#[serde(flatten)]",
|
||||
)
|
||||
.field_attribute(
|
||||
"dcs.mission.v0.StreamEventsResponse.MarkChangeEvent.visibility",
|
||||
"#[serde(flatten)]",
|
||||
)
|
||||
.field_attribute(
|
||||
"dcs.mission.v0.StreamEventsResponse.MarkRemoveEvent.visibility",
|
||||
"#[serde(flatten)]",
|
||||
)
|
||||
.field_attribute(
|
||||
"dcs.mission.v0.AddMissionCommandRequest.details",
|
||||
r#"#[serde(with = "crate::dcs::utils::proto_struct")]"#,
|
||||
)
|
||||
.field_attribute(
|
||||
"dcs.mission.v0.StreamEventsResponse.MissionCommandEvent.details",
|
||||
r#"#[serde(with = "crate::dcs::utils::proto_struct")]"#,
|
||||
)
|
||||
.field_attribute(
|
||||
"dcs.mission.v0.AddCoalitionCommandRequest.details",
|
||||
r#"#[serde(with = "crate::dcs::utils::proto_struct")]"#,
|
||||
)
|
||||
.field_attribute(
|
||||
"dcs.mission.v0.StreamEventsResponse.CoalitionCommandEvent.details",
|
||||
r#"#[serde(with = "crate::dcs::utils::proto_struct")]"#,
|
||||
)
|
||||
.field_attribute(
|
||||
"dcs.mission.v0.AddGroupCommandRequest.details",
|
||||
r#"#[serde(with = "crate::dcs::utils::proto_struct")]"#,
|
||||
)
|
||||
.field_attribute(
|
||||
"dcs.mission.v0.StreamEventsResponse.GroupCommandEvent.details",
|
||||
r#"#[serde(with = "crate::dcs::utils::proto_struct")]"#,
|
||||
)
|
||||
.build_client(true)
|
||||
.compile(&["protos/dcs/dcs.proto"], &["protos"])?;
|
||||
Ok(())
|
||||
}
|
||||
61
crates/dcs-grpc/protos/dcs/atmosphere/v0/atmosphere.proto
Normal file
61
crates/dcs-grpc/protos/dcs/atmosphere/v0/atmosphere.proto
Normal file
@@ -0,0 +1,61 @@
|
||||
syntax = "proto3";
|
||||
package dcs.atmosphere.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Atmosphere";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/atmosphere";
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_singleton_atmosphere
|
||||
service AtmosphereService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getWind
|
||||
rpc GetWind(GetWindRequest) returns (GetWindResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getWindWithTurbulence
|
||||
rpc GetWindWithTurbulence(GetWindWithTurbulenceRequest)
|
||||
returns (GetWindWithTurbulenceResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getWindWithTurbulence
|
||||
rpc GetTemperatureAndPressure(GetTemperatureAndPressureRequest)
|
||||
returns (GetTemperatureAndPressureResponse) {}
|
||||
}
|
||||
|
||||
message GetWindRequest {
|
||||
// The position on the map we want the wind information for.
|
||||
// Requires lat/lon/alt fields to be populated, there are
|
||||
// no default values
|
||||
dcs.common.v0.InputPosition position = 1;
|
||||
}
|
||||
|
||||
message GetWindResponse {
|
||||
// The heading the wind is coming from.
|
||||
float heading = 1;
|
||||
// The strength of the wind in meters per second
|
||||
float strength = 2;
|
||||
}
|
||||
|
||||
message GetWindWithTurbulenceRequest {
|
||||
// The position on the map we want the wind information for.
|
||||
// Requires lat/lon/alt fields to be populated, there are
|
||||
// no default values
|
||||
dcs.common.v0.InputPosition position = 1;
|
||||
}
|
||||
|
||||
message GetWindWithTurbulenceResponse {
|
||||
// The heading the wind is coming from.
|
||||
float heading = 1;
|
||||
// The strength of the wind in meters per second.
|
||||
float strength = 2;
|
||||
}
|
||||
|
||||
message GetTemperatureAndPressureRequest {
|
||||
// The position on the map we want the wind information for.
|
||||
// Requires lat/lon/alt fields to be populated, there are
|
||||
// no default values
|
||||
dcs.common.v0.InputPosition position = 1;
|
||||
}
|
||||
|
||||
message GetTemperatureAndPressureResponse {
|
||||
// The temperature in Kelvin
|
||||
float temperature = 1;
|
||||
// The pressure in Pascals
|
||||
float pressure = 2;
|
||||
}
|
||||
226
crates/dcs-grpc/protos/dcs/coalition/v0/coalition.proto
Normal file
226
crates/dcs-grpc/protos/dcs/coalition/v0/coalition.proto
Normal file
@@ -0,0 +1,226 @@
|
||||
syntax = "proto3";
|
||||
package dcs.coalition.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Coalition";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/coalition";
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_singleton_coalition
|
||||
service CoalitionService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_addGroup
|
||||
rpc AddGroup(AddGroupRequest) returns (AddGroupResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getStaticObjects
|
||||
rpc GetStaticObjects(GetStaticObjectsRequest)
|
||||
returns (GetStaticObjectsResponse) {}
|
||||
|
||||
// Focussed on statics (linked statics - see `AddLinkedStatic`)
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_addStaticObject
|
||||
rpc AddStaticObject(AddStaticObjectRequest)
|
||||
returns (AddStaticObjectResponse) {}
|
||||
|
||||
// Focussed on properties relevant to linked static objects
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_addStaticObject
|
||||
rpc AddLinkedStatic(AddLinkedStaticRequest)
|
||||
returns (AddLinkedStaticResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getGroups
|
||||
rpc GetGroups(GetGroupsRequest) returns (GetGroupsResponse) {}
|
||||
|
||||
/*
|
||||
* Get the Bullseye for the coalition
|
||||
*
|
||||
* This position is set at mission start and does not change for the duration
|
||||
* of the mission.
|
||||
*
|
||||
* See https://wiki.hoggitworld.com/view/DCS_func_getMainRefPoint for more
|
||||
* details
|
||||
*/
|
||||
rpc GetBullseye(GetBullseyeRequest) returns (GetBullseyeResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getPlayers
|
||||
rpc GetPlayerUnits(GetPlayerUnitsRequest) returns (GetPlayerUnitsResponse) {}
|
||||
}
|
||||
|
||||
message AddGroupRequest {
|
||||
// The coalition is determined by the provided Country
|
||||
// and the coalition setup of the mission
|
||||
dcs.common.v0.Country country = 2;
|
||||
dcs.common.v0.GroupCategory group_category = 3;
|
||||
oneof template {
|
||||
GroundGroupTemplate ground_template = 4;
|
||||
ShipGroupTemplate ship_template = 5;
|
||||
HelicopterGroupTemplate helicopter_template = 6;
|
||||
PlaneGroupTemplate plane_template = 7;
|
||||
}
|
||||
|
||||
message GroundGroupTemplate {
|
||||
optional uint32 group_id = 1;
|
||||
bool hidden = 2;
|
||||
bool late_activation = 3;
|
||||
string name = 4;
|
||||
dcs.common.v0.InputPosition position = 5;
|
||||
repeated Point waypoints = 6;
|
||||
uint32 start_time = 7;
|
||||
string task = 8;
|
||||
bool task_selected = 9;
|
||||
repeated Task tasks = 10;
|
||||
bool uncontrollable = 11;
|
||||
repeated GroundUnitTemplate units = 12;
|
||||
bool visible = 13;
|
||||
}
|
||||
message GroundUnitTemplate {
|
||||
string name = 1;
|
||||
string type = 2;
|
||||
dcs.common.v0.InputPosition position = 3;
|
||||
optional uint32 unit_id = 4;
|
||||
optional uint32 heading = 5;
|
||||
Skill skill = 6;
|
||||
}
|
||||
|
||||
message ShipGroupTemplate {
|
||||
}
|
||||
message ShipUnitTemplate {
|
||||
}
|
||||
|
||||
message HelicopterGroupTemplate {
|
||||
}
|
||||
message HelicopterUnitTemplate {
|
||||
}
|
||||
|
||||
message PlaneGroupTemplate {
|
||||
}
|
||||
message PlaneUnitTemplate {
|
||||
}
|
||||
|
||||
message Point {
|
||||
enum AltitudeType {
|
||||
ALTITUDE_TYPE_UNSPECIFIED = 0;
|
||||
ALTITUDE_TYPE_BAROMETRIC = 1;
|
||||
ALTITUDE_TYPE_RADIO = 2;
|
||||
}
|
||||
|
||||
enum PointType {
|
||||
// protolint:disable:next ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
|
||||
POINT_TYPE_RANDOM = 0;
|
||||
POINT_TYPE_TAKEOFF = 1;
|
||||
POINT_TYPE_TAKEOFF_PARKING = 2;
|
||||
POINT_TYPE_TURNING_POINT = 3;
|
||||
POINT_TYPE_TAKEOFF_PARKING_HOT = 4;
|
||||
POINT_TYPE_LAND = 5;
|
||||
}
|
||||
|
||||
dcs.common.v0.InputPosition position = 1;
|
||||
AltitudeType altitude_type = 2;
|
||||
PointType type = 3;
|
||||
string action = 4;
|
||||
string form = 5;
|
||||
double speed = 6;
|
||||
}
|
||||
|
||||
enum Skill {
|
||||
// protolint:disable:next ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
|
||||
SKILL_RANDOM = 0;
|
||||
SKILL_AVERAGE = 1;
|
||||
SKILL_GOOD = 2;
|
||||
SKILL_HIGH = 3;
|
||||
SKILL_EXCELLENT = 4;
|
||||
SKILL_PLAYER = 5;
|
||||
}
|
||||
|
||||
message Task {
|
||||
}
|
||||
}
|
||||
|
||||
message AddGroupResponse {
|
||||
dcs.common.v0.Group group = 1;
|
||||
}
|
||||
|
||||
message GetStaticObjectsRequest {
|
||||
// the coalition which the statics belong to
|
||||
dcs.common.v0.Coalition coalition = 1;
|
||||
}
|
||||
|
||||
message GetStaticObjectsResponse {
|
||||
// the list of statics
|
||||
repeated dcs.common.v0.Static statics = 1;
|
||||
}
|
||||
|
||||
message AddStaticObjectRequest {
|
||||
// the name of the static; must be unique or would destroy previous object
|
||||
string name = 1;
|
||||
// country the unit belongs to
|
||||
dcs.common.v0.Country country = 2;
|
||||
// type of the static object (e.g. "Farm A", "AS32-31A")
|
||||
string type = 3;
|
||||
// string name of the livery for the aircraft
|
||||
string livery = 4;
|
||||
// boolean for whether or not the object will appear as a wreck
|
||||
bool dead = 5;
|
||||
// number value for the "score" of the object when it is killed
|
||||
optional uint32 rate = 6;
|
||||
|
||||
double heading = 7;
|
||||
dcs.common.v0.InputPosition position = 8;
|
||||
// cargo mass in kilograms
|
||||
uint32 cargo_mass = 9;
|
||||
}
|
||||
|
||||
message AddStaticObjectResponse {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message AddLinkedStaticRequest {
|
||||
// the name of the static; must be unique or would destroy previous object
|
||||
string name = 1;
|
||||
// country the unit belongs to
|
||||
dcs.common.v0.Country country = 2;
|
||||
// type of the static object (e.g. "Farm A", "AS32-31A")
|
||||
string type = 3;
|
||||
// string name of the livery for the aircraft
|
||||
string livery = 4;
|
||||
// boolean for whether or not the object will appear as a wreck
|
||||
bool dead = 5;
|
||||
// number value for the "score" of the object when it is killed
|
||||
optional uint32 rate = 6;
|
||||
// the name of the unit to offset from
|
||||
string unit = 7;
|
||||
// the angle to relative to the linked unit, in a clockwise direction.
|
||||
// negative values are anti-clockwise
|
||||
double angle = 8;
|
||||
// x offset from linked unit center (positive is forward; negative is aft)
|
||||
double x = 9;
|
||||
// y offset from linked unit center (positive is starboard-side;
|
||||
// negative is port-side)
|
||||
double y = 10;
|
||||
}
|
||||
|
||||
message AddLinkedStaticResponse {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetGroupsRequest {
|
||||
dcs.common.v0.Coalition coalition = 1;
|
||||
dcs.common.v0.GroupCategory category = 2;
|
||||
}
|
||||
|
||||
message GetGroupsResponse {
|
||||
repeated dcs.common.v0.Group groups = 1;
|
||||
}
|
||||
|
||||
message GetBullseyeRequest {
|
||||
// A specific coalition must be used for this API call. Do not use
|
||||
// `COALITION_ALL`
|
||||
dcs.common.v0.Coalition coalition = 1;
|
||||
}
|
||||
|
||||
message GetBullseyeResponse {
|
||||
dcs.common.v0.Position position = 1;
|
||||
}
|
||||
|
||||
message GetPlayerUnitsRequest {
|
||||
dcs.common.v0.Coalition coalition = 1;
|
||||
}
|
||||
|
||||
message GetPlayerUnitsResponse {
|
||||
repeated dcs.common.v0.Unit units = 1;
|
||||
}
|
||||
472
crates/dcs-grpc/protos/dcs/common/v0/common.proto
Normal file
472
crates/dcs-grpc/protos/dcs/common/v0/common.proto
Normal file
@@ -0,0 +1,472 @@
|
||||
syntax = "proto3";
|
||||
package dcs.common.v0;
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Common";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/common";
|
||||
|
||||
/**
|
||||
* The category the object belongs to
|
||||
*
|
||||
* All DCS objects are one of the following categories. Unlike many other
|
||||
* enums created by DCS, this one is not 0 indexed. Therefore we do not
|
||||
* need to do any modification of the value by incrementing it by one to
|
||||
* make it work with gRPC and DCS.
|
||||
*
|
||||
* See https://wiki.hoggitworld.com/view/DCS_Class_Object for more information
|
||||
*/
|
||||
enum ObjectCategory {
|
||||
OBJECT_CATEGORY_UNSPECIFIED = 0;
|
||||
OBJECT_CATEGORY_UNIT = 1;
|
||||
OBJECT_CATEGORY_WEAPON = 2;
|
||||
OBJECT_CATEGORY_STATIC = 3;
|
||||
OBJECT_CATEGORY_SCENERY = 4;
|
||||
OBJECT_CATEGORY_BASE = 5;
|
||||
OBJECT_CATEGORY_CARGO = 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* The category the object belongs to
|
||||
*
|
||||
* Some of these are less than obvious. For example an oilrig counts as a
|
||||
* HELIPAD airfield.
|
||||
*/
|
||||
enum AirbaseCategory {
|
||||
AIRBASE_CATEGORY_UNSPECIFIED = 0;
|
||||
AIRBASE_CATEGORY_AIRDROME = 1;
|
||||
AIRBASE_CATEGORY_HELIPAD = 2;
|
||||
AIRBASE_CATEGORY_SHIP = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coalitions in DCS
|
||||
*
|
||||
* The coalitions supported by DCS. The NEUTRAL coalition is a relatively new
|
||||
* one and may not be as supported as the belligerant ones.
|
||||
*/
|
||||
enum Coalition {
|
||||
// protolint:disable:next ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
|
||||
COALITION_ALL = 0;
|
||||
COALITION_NEUTRAL = 1;
|
||||
COALITION_RED = 2;
|
||||
COALITION_BLUE = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Countries in DCS
|
||||
*
|
||||
* Every country belongs to a coalition and this association is set per mission.
|
||||
* The values of these enums are correct such that they will work with DCS
|
||||
* however the text names have been Made to follow gRPC conventions to to aid
|
||||
* in language bindings and acronyms have been replaced with their full english
|
||||
* names to aid in recognition. In some cases this can be a big change
|
||||
* (e.g. USSR -> Soviet Union).
|
||||
*
|
||||
* We have also added a dummy value for the missing enum value 14 to prevent
|
||||
* possible issues in the various language bindings
|
||||
*
|
||||
* See https://wiki.hoggitworld.com/view/DCS_enum_country for more information
|
||||
*/
|
||||
enum Country {
|
||||
COUNTRY_UNSPECIFIED = 0;
|
||||
COUNTRY_RUSSIA = 1;
|
||||
COUNTRY_UKRAINE = 2;
|
||||
COUNTRY_UNITED_STATES_OF_AMERICA = 3;
|
||||
COUNTRY_TURKEY = 4;
|
||||
COUNTRY_UNITED_KINGDOM = 5;
|
||||
COUNTRY_FRANCE = 6;
|
||||
COUNTRY_GERMANY = 7;
|
||||
COUNTRY_AGGRESSORS = 8;
|
||||
COUNTRY_CANADA = 9;
|
||||
COUNTRY_SPAIN = 10;
|
||||
COUNTRY_THE_NETHERLANDS = 11;
|
||||
COUNTRY_BELGIUM = 12;
|
||||
COUNTRY_NORWAY = 13;
|
||||
COUNTRY_DENMARK = 14;
|
||||
COUNTRY_UNUSED = 15;
|
||||
COUNTRY_ISRAEL = 16;
|
||||
COUNTRY_GEORGIA = 17;
|
||||
COUNTRY_INSURGENTS = 18;
|
||||
COUNTRY_ABKHAZIA = 19;
|
||||
COUNTRY_SOUTH_OSETIA = 20;
|
||||
COUNTRY_ITALY = 21;
|
||||
COUNTRY_AUSTRALIA = 22;
|
||||
COUNTRY_SWITZERLAND = 23;
|
||||
COUNTRY_AUSTRIA = 24;
|
||||
COUNTRY_BELARUS = 25;
|
||||
COUNTRY_BULGARIA = 26;
|
||||
COUNTRY_CZECH_REPUBLIC = 27;
|
||||
COUNTRY_CHINA = 28;
|
||||
COUNTRY_CROATIA = 29;
|
||||
COUNTRY_EGYPT = 30;
|
||||
COUNTRY_FINLAND = 31;
|
||||
COUNTRY_GREECE = 32;
|
||||
COUNTRY_HUNGARY = 33;
|
||||
COUNTRY_INDIA = 34;
|
||||
COUNTRY_IRAN = 35;
|
||||
COUNTRY_IRAQ = 36;
|
||||
COUNTRY_JAPAN = 37;
|
||||
COUNTRY_KAZAKHSTAN = 38;
|
||||
COUNTRY_NORTH_KOREA = 39;
|
||||
COUNTRY_PAKISTAN = 40;
|
||||
COUNTRY_POLAND = 41;
|
||||
COUNTRY_ROMANIA = 42;
|
||||
COUNTRY_SAUDI_ARABIA = 43;
|
||||
COUNTRY_SERBIA = 44;
|
||||
COUNTRY_SLOVAKIA = 45;
|
||||
COUNTRY_SOUTH_KOREA = 46;
|
||||
COUNTRY_SWEDEN = 47;
|
||||
COUNTRY_SYRIA = 48;
|
||||
COUNTRY_YEMEN = 49;
|
||||
COUNTRY_VIETNAM = 50;
|
||||
COUNTRY_VENEZUELA = 51;
|
||||
COUNTRY_TUNISIA = 52;
|
||||
COUNTRY_THAILAND = 53;
|
||||
COUNTRY_SUDAN = 54;
|
||||
COUNTRY_PHILIPPINES = 55;
|
||||
COUNTRY_MOROCCO = 56;
|
||||
COUNTRY_MEXICO = 57;
|
||||
COUNTRY_MALAYSIA = 58;
|
||||
COUNTRY_LIBYA = 59;
|
||||
COUNTRY_JORDAN = 60;
|
||||
COUNTRY_INDONESIA = 61;
|
||||
COUNTRY_HONDURAS = 62;
|
||||
COUNTRY_ETHIOPIA = 63;
|
||||
COUNTRY_CHILE = 64;
|
||||
COUNTRY_BRAZIL = 65;
|
||||
COUNTRY_BAHRAIN = 66;
|
||||
COUNTRY_THIRDREICH = 67;
|
||||
COUNTRY_YUGOSLAVIA = 68;
|
||||
COUNTRY_SOVIET_UNION = 69;
|
||||
COUNTRY_ITALIAN_SOCIAL_REPUBLIC = 70;
|
||||
COUNTRY_ALGERIA = 71;
|
||||
COUNTRY_KUWAIT = 72;
|
||||
COUNTRY_QATAR = 73;
|
||||
COUNTRY_OMAN = 74;
|
||||
COUNTRY_UNITED_ARAB_EMIRATES = 75;
|
||||
COUNTRY_SOUTH_AFRICA = 76;
|
||||
COUNTRY_CUBA = 77;
|
||||
COUNTRY_PORTUGAL = 78;
|
||||
COUNTRY_GERMAN_DEMOCRATIC_REPUBLIC = 79;
|
||||
COUNTRY_LEBANON = 80;
|
||||
COUNTRY_COMBINED_JOINT_TASK_FORCE_BLUE = 81;
|
||||
COUNTRY_COMBINED_JOINT_TASK_FORCE_RED = 82;
|
||||
COUNTRY_UNITED_NATIONS_PEACEKEEPERS = 83;
|
||||
COUNTRY_ARGENTINA = 84;
|
||||
COUNTRY_CYPRUS = 85;
|
||||
COUNTRY_SLOVENIA = 86;
|
||||
}
|
||||
|
||||
/**
|
||||
* Position of an object in DCS
|
||||
*
|
||||
* Latitude and Longitude are in Decimal Degrees format (e.g. 41.33 / 37.21).
|
||||
* Negative values are used for West of the meridian and south of the equator
|
||||
*
|
||||
* Altitude is given in meters above Mean Sea Level (MSL) and can be a decimal
|
||||
* value.
|
||||
*/
|
||||
message Position {
|
||||
// Latitude in Decimal Degrees format
|
||||
double lat = 1;
|
||||
// Longitude in Decimal Degrees format
|
||||
double lon = 2;
|
||||
// Altitude in Meters above Mean Sea Level (MSL)
|
||||
double alt = 3;
|
||||
// Distance between DCS' map origin to object in meters on west-east axis.
|
||||
double u = 4;
|
||||
// Distance between DCS' map origin to object in meters on north-south axis.
|
||||
double v = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Position used in requests to DCS-gRPC.
|
||||
*
|
||||
* Latitude and Longitude are in Decimal Degrees format (e.g. 41.33 / 37.21).
|
||||
* Negative values are used for West of the meridian and south of the equator.
|
||||
*
|
||||
* Altitude is given in meters above Mean Sea Level (MSL) and can be a decimal
|
||||
* value.
|
||||
*/
|
||||
message InputPosition {
|
||||
// Latitude in Decimal Degrees format
|
||||
double lat = 1;
|
||||
// Longitude in Decimal Degrees format
|
||||
double lon = 2;
|
||||
// Altitude in Meters above Mean Sea Level (MSL)
|
||||
double alt = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* This type is returned if an object category cannot be determined
|
||||
*
|
||||
* The base object includes the `getName()` function so even for an unknown type
|
||||
* we _should_ be able to get the name
|
||||
*/
|
||||
message Unknown {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of a DCS Unit
|
||||
*
|
||||
* A unit is an "active" unit in a DCS mission. This means it has an attached AI
|
||||
* that moves and shoots. Units include aircraft, ground units, ships, weapons
|
||||
* etc.
|
||||
*/
|
||||
message Unit {
|
||||
// The DCS generated ID
|
||||
uint32 id = 1;
|
||||
// The name of the unit as assigned in the mission editor
|
||||
string name = 2;
|
||||
// The DCS assigned callsign if one exists. e.g. "Enfield 11"
|
||||
string callsign = 3;
|
||||
// The coalition the unit belongs to
|
||||
Coalition coalition = 4;
|
||||
// The DCS type-name of the unit. e.g "MiG-29A", "ZSU_57_2" or "Hawk ln"
|
||||
string type = 5;
|
||||
// The position of the unit
|
||||
Position position = 6;
|
||||
// The orientation of the unit in both 2D and 3D space
|
||||
Orientation orientation = 7;
|
||||
// The velocity of the unit in both 2D and 3D space
|
||||
Velocity velocity = 8;
|
||||
// The name of the player if one is in control of the unit
|
||||
optional string player_name = 9;
|
||||
// The group that the unit belongs to
|
||||
Group group = 10;
|
||||
// The number of this unit in the group. Does not change as units are
|
||||
// destroyed
|
||||
uint32 number_in_group = 11;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of a DCS group
|
||||
*/
|
||||
message Group {
|
||||
uint32 id = 1; // The DCS generated ID
|
||||
string name = 2; // The name of the group as assigned in the mission editor
|
||||
Coalition coalition = 3; // The coalition of the group
|
||||
GroupCategory category = 4; // The group category.
|
||||
}
|
||||
|
||||
/**
|
||||
* Group category enumerator.
|
||||
*/
|
||||
enum GroupCategory {
|
||||
GROUP_CATEGORY_UNSPECIFIED = 0;
|
||||
GROUP_CATEGORY_AIRPLANE = 1;
|
||||
GROUP_CATEGORY_HELICOPTER = 2;
|
||||
GROUP_CATEGORY_GROUND = 3;
|
||||
GROUP_CATEGORY_SHIP = 4;
|
||||
GROUP_CATEGORY_TRAIN = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of a DCS weapon
|
||||
*
|
||||
* These weapons include everything from autocannon HE shells up to massive
|
||||
* ship-killer missiles
|
||||
*/
|
||||
message Weapon {
|
||||
// The DCS generated ID
|
||||
uint32 id = 1;
|
||||
// The DCS type-name of the weapon. e.g "Matra_S530D", "HAWK_RAKETA" or
|
||||
// "weapons.shells.53-UOR-281U"
|
||||
string type = 2;
|
||||
// The position of the Weapon
|
||||
Position position = 3;
|
||||
// The orientation of the unit in both 2D and 3D space
|
||||
Orientation orientation = 4;
|
||||
// The velocity of the unit in both 2D and 3D space
|
||||
Velocity velocity = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of a DCS static object
|
||||
*
|
||||
* These objects are often buildings but can also be vehicles that have no AI or
|
||||
* other game behaviour aside from being destroyable
|
||||
*/
|
||||
message Static {
|
||||
// The DCS generated ID
|
||||
uint32 id = 1;
|
||||
// The DCS type-name of the static
|
||||
string type = 2;
|
||||
// The name of the static
|
||||
string name = 3;
|
||||
// The coalition the static belongs to
|
||||
Coalition coalition = 4;
|
||||
// The position of the static
|
||||
Position position = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of a DCS scenery object
|
||||
*/
|
||||
message Scenery {
|
||||
// The id of the scenery
|
||||
uint32 id = 1;
|
||||
// The DCS type-name of the scenery
|
||||
string type = 2;
|
||||
// The position of the scenery
|
||||
Position position = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of a DCS Airfield
|
||||
*
|
||||
*/
|
||||
message Airbase {
|
||||
// Information about the unit, if the airbase is one (e.g. in case of a
|
||||
// carrier).
|
||||
optional Unit unit = 1;
|
||||
// TODO: Fill this in
|
||||
string name = 2;
|
||||
// TODO: Fill this in
|
||||
string callsign = 3;
|
||||
// The coalition the unit belongs to. This can change mid-mission if an
|
||||
// airfield is captured
|
||||
Coalition coalition = 4;
|
||||
// The position of the center point of the airfield.
|
||||
Position position = 6;
|
||||
// What category the airfield belongs to.
|
||||
AirbaseCategory category = 7;
|
||||
// TODO: Fill this in
|
||||
string display_name = 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of a DCS Cargo object
|
||||
*/
|
||||
message Cargo {
|
||||
}
|
||||
|
||||
/*
|
||||
* The initiator of an event
|
||||
*
|
||||
* The initiator of an event. For things like shooting events it is usually a
|
||||
* vehicle but it can be almost anything depending on the event
|
||||
*/
|
||||
message Initiator {
|
||||
oneof initiator {
|
||||
Unknown unknown = 1;
|
||||
Unit unit = 2;
|
||||
Weapon weapon = 3;
|
||||
Static static = 4;
|
||||
Scenery scenery = 5;
|
||||
Airbase airbase = 6;
|
||||
Cargo cargo = 7;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The target of an event
|
||||
*
|
||||
* The target of an event. For things like shooting events it is usually a
|
||||
* vehicle but it can be almost anything depending on the event
|
||||
*/
|
||||
message Target {
|
||||
oneof target {
|
||||
Unknown unknown = 1;
|
||||
Unit unit = 2;
|
||||
Weapon weapon = 3;
|
||||
Static static = 4;
|
||||
Scenery scenery = 5;
|
||||
Airbase airbase = 6;
|
||||
Cargo cargo = 7;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A MarkPanel
|
||||
*
|
||||
* A MarkPanel visible on the F10 map. These can be used for reference by
|
||||
* players but can also be used by things like Jester for setting waypoints
|
||||
*/
|
||||
message MarkPanel {
|
||||
// The id of the mark panel.
|
||||
uint32 id = 1;
|
||||
// The time in seconds relative to the mission start the mark got created.
|
||||
double time = 2;
|
||||
// The unit of the player that created the mark. Not set if the player isn't
|
||||
// controlling any unit anymore (disconnected, spectator, game master, ...).
|
||||
optional Unit initiator = 3;
|
||||
// If set, the mark is only visible for the specified coalition.
|
||||
optional Coalition coalition = 4;
|
||||
// The ID of the group the player was in when creating the mark panel. This
|
||||
// will still be set even if the player isn't controlling the unit in that
|
||||
// group anymore.
|
||||
optional uint32 group_id = 5;
|
||||
// The text content of the mark.
|
||||
optional string text = 6;
|
||||
// The position of the mark.
|
||||
Position position = 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* A vector in a right-handed coordinate system where +x is north, -x south, +z
|
||||
* is east, -z west, +y up and -y down.
|
||||
*/
|
||||
message Vector {
|
||||
double x = 1;
|
||||
double y = 2;
|
||||
double z = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* The orientation of an object in 3D space.
|
||||
*/
|
||||
message Orientation {
|
||||
// The heading the nose of the object points to on a flat world.
|
||||
double heading = 1;
|
||||
// Yaw in degrees - clockwise relative to the true north (this is similar to
|
||||
// the heading, just corrected by the projection error when going from a flat
|
||||
// to a spherical world).
|
||||
double yaw = 2;
|
||||
// Pitch in degrees - positive when taking-off.
|
||||
double pitch = 3;
|
||||
// Roll in degrees - positive when rolling the aircraft to the right.
|
||||
double roll = 4;
|
||||
// The normalized direction the object is pointing to.
|
||||
Vector forward = 5;
|
||||
// The normalized direction the three line (right wing) is pointing to.
|
||||
Vector right = 6;
|
||||
// The normalized up vector (orthogonal to forward and right).
|
||||
Vector up = 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* The orientation of an object in 3D space.
|
||||
*/
|
||||
message Velocity {
|
||||
// The heading the object is moving to (use `orientation.heading` to get the
|
||||
// heading the nose is pointing to).
|
||||
double heading = 1;
|
||||
// The horizontal speed of the unit. If it is doing mach one straight up then
|
||||
// the speed will be 0
|
||||
double speed = 2;
|
||||
// The direction the object is traveling to, and speed (magnitude of the
|
||||
// vector) the object is traveling with.
|
||||
Vector velocity = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of a contact in a DCS AI controller's detection table
|
||||
*
|
||||
* This is a target that the AI controller has detected and is actively tracking
|
||||
*
|
||||
*/
|
||||
message Contact {
|
||||
// The DCS generated ID
|
||||
uint32 id = 1;
|
||||
// Can the sensor see the contact
|
||||
bool visible = 2;
|
||||
// Does the controller know the distance to the contact?
|
||||
bool distance = 3;
|
||||
|
||||
// Either the basic information, or Unit or Weapon export object
|
||||
oneof target {
|
||||
Unknown object = 4;
|
||||
Unit unit = 5;
|
||||
Weapon weapon = 6;
|
||||
}
|
||||
}
|
||||
51
crates/dcs-grpc/protos/dcs/controller/v0/controller.proto
Normal file
51
crates/dcs-grpc/protos/dcs/controller/v0/controller.proto
Normal file
@@ -0,0 +1,51 @@
|
||||
syntax = "proto3";
|
||||
package dcs.controller.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Controller";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/controller";
|
||||
|
||||
service ControllerService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_option_alarmState
|
||||
rpc SetAlarmState(SetAlarmStateRequest) returns (SetAlarmStateResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getDetectedTargets
|
||||
rpc GetDetectedTargets(GetDetectedTargetsRequest)
|
||||
returns (GetDetectedTargetsResponse) {}
|
||||
}
|
||||
|
||||
message SetAlarmStateRequest {
|
||||
enum AlarmState {
|
||||
ALARM_STATE_UNSPECIFIED = 0;
|
||||
ALARM_STATE_AUTO = 1;
|
||||
ALARM_STATE_GREEN = 2;
|
||||
ALARM_STATE_RED = 3;
|
||||
}
|
||||
|
||||
oneof name {
|
||||
string group_name = 1;
|
||||
string unit_name = 2;
|
||||
}
|
||||
AlarmState alarm_state = 3;
|
||||
}
|
||||
|
||||
message SetAlarmStateResponse {
|
||||
}
|
||||
|
||||
message GetDetectedTargetsRequest {
|
||||
enum DetectionType {
|
||||
DETECTION_TYPE_UNSPECIFIED = 0;
|
||||
DETECTION_TYPE_VISUAL = 1;
|
||||
DETECTION_TYPE_OPTIC = 2;
|
||||
DETECTION_TYPE_RADAR = 4;
|
||||
DETECTION_TYPE_IRST = 8;
|
||||
DETECTION_TYPE_RWR = 16;
|
||||
DETECTION_TYPE_DLINK = 32;
|
||||
}
|
||||
string unit_name = 1;
|
||||
optional bool include_object = 2;
|
||||
optional DetectionType detection_type = 3;
|
||||
}
|
||||
|
||||
message GetDetectedTargetsResponse {
|
||||
repeated dcs.common.v0.Contact contacts = 1;
|
||||
}
|
||||
91
crates/dcs-grpc/protos/dcs/custom/v0/custom.proto
Normal file
91
crates/dcs-grpc/protos/dcs/custom/v0/custom.proto
Normal file
@@ -0,0 +1,91 @@
|
||||
syntax = "proto3";
|
||||
package dcs.custom.v0;
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Custom";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/custom";
|
||||
|
||||
// The Custom service is for APIs that do not map to the "standard library" of
|
||||
// DCS APIs provided by Eagle Dynamics.
|
||||
//
|
||||
// Expect to find APIs here that may be useful for mission frameworks etc.
|
||||
service CustomService {
|
||||
// DCT Function
|
||||
rpc RequestMissionAssignment(RequestMissionAssignmentRequest)
|
||||
returns (RequestMissionAssignmentResponse) {}
|
||||
|
||||
// DCT Function
|
||||
rpc JoinMission(JoinMissionRequest) returns (JoinMissionResponse) {}
|
||||
|
||||
// DCT Function
|
||||
rpc AbortMission(AbortMissionRequest) returns (AbortMissionResponse) {}
|
||||
|
||||
// DCT Function
|
||||
rpc GetMissionStatus(GetMissionStatusRequest)
|
||||
returns (GetMissionStatusResponse) {}
|
||||
|
||||
// Evaluate some Lua inside of the mission and return the result as a JSON
|
||||
// string. Disabled by default.
|
||||
rpc Eval(EvalRequest) returns (EvalResponse) {}
|
||||
|
||||
/**
|
||||
* Calculates the magnetic declination at the given position using the
|
||||
* International Geomagnetic Reference Field (IGRF) model. The result is not
|
||||
* always exactly the same as what DCS seem to use, but it is very close (DCS
|
||||
* doesn't expose its declination).
|
||||
*/
|
||||
rpc GetMagneticDeclination(GetMagneticDeclinationRequest)
|
||||
returns (GetMagneticDeclinationResponse) {}
|
||||
}
|
||||
|
||||
message RequestMissionAssignmentRequest {
|
||||
string unit_name = 1;
|
||||
string mission_type = 2;
|
||||
}
|
||||
|
||||
message RequestMissionAssignmentResponse {
|
||||
}
|
||||
|
||||
message JoinMissionRequest {
|
||||
string unit_name = 1;
|
||||
int32 mission_code = 2;
|
||||
}
|
||||
|
||||
message JoinMissionResponse {
|
||||
}
|
||||
|
||||
message AbortMissionRequest {
|
||||
string unit_name = 1;
|
||||
}
|
||||
|
||||
message AbortMissionResponse {
|
||||
}
|
||||
|
||||
message GetMissionStatusRequest {
|
||||
string unit_name = 1;
|
||||
}
|
||||
|
||||
message GetMissionStatusResponse {
|
||||
}
|
||||
|
||||
message EvalRequest {
|
||||
string lua = 1;
|
||||
}
|
||||
|
||||
message EvalResponse {
|
||||
string json = 1;
|
||||
}
|
||||
|
||||
message GetMagneticDeclinationRequest {
|
||||
/// Latitude in Decimal Degrees format
|
||||
double lat = 1;
|
||||
/// Longitude in Decimal Degrees format
|
||||
double lon = 2;
|
||||
/// Altitude in Meters above Mean Sea Level (MSL)
|
||||
double alt = 3;
|
||||
}
|
||||
|
||||
message GetMagneticDeclinationResponse {
|
||||
/// Magnetic declination in degrees. A negative value is an westerly
|
||||
/// declination, while a positive value is a easterly declination. `True
|
||||
/// North` + `declination` = `Magnetic North`
|
||||
double declination = 1;
|
||||
}
|
||||
18
crates/dcs-grpc/protos/dcs/dcs.proto
Normal file
18
crates/dcs-grpc/protos/dcs/dcs.proto
Normal file
@@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package dcs;
|
||||
|
||||
import "dcs/atmosphere/v0/atmosphere.proto";
|
||||
import "dcs/coalition/v0/coalition.proto";
|
||||
import "dcs/common/v0/common.proto";
|
||||
import "dcs/controller/v0/controller.proto";
|
||||
import "dcs/custom/v0/custom.proto";
|
||||
import "dcs/group/v0/group.proto";
|
||||
import "dcs/hook/v0/hook.proto";
|
||||
import "dcs/mission/v0/mission.proto";
|
||||
import "dcs/net/v0/net.proto";
|
||||
import "dcs/srs/v0/srs.proto";
|
||||
import "dcs/timer/v0/timer.proto";
|
||||
import "dcs/trigger/v0/trigger.proto";
|
||||
import "dcs/unit/v0/unit.proto";
|
||||
import "dcs/world/v0/world.proto";
|
||||
42
crates/dcs-grpc/protos/dcs/group/v0/group.proto
Normal file
42
crates/dcs-grpc/protos/dcs/group/v0/group.proto
Normal file
@@ -0,0 +1,42 @@
|
||||
syntax = "proto3";
|
||||
package dcs.group.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Group";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/group";
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_Class_Group
|
||||
service GroupService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getUnits
|
||||
rpc GetUnits(GetUnitsRequest) returns (GetUnitsResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_activate
|
||||
rpc Activate(ActivateRequest) returns (ActivateResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_destroy
|
||||
rpc Destroy(DestroyRequest) returns (DestroyResponse) {}
|
||||
}
|
||||
|
||||
message GetUnitsRequest {
|
||||
string group_name = 1;
|
||||
// Whether the response should include only active units (`true`), only
|
||||
// inactive units (`false`), or all units (`nil`).
|
||||
optional bool active = 2;
|
||||
}
|
||||
|
||||
message GetUnitsResponse {
|
||||
repeated dcs.common.v0.Unit units = 1;
|
||||
}
|
||||
|
||||
message ActivateRequest {
|
||||
string group_name = 1;
|
||||
}
|
||||
|
||||
message ActivateResponse {
|
||||
}
|
||||
|
||||
message DestroyRequest {
|
||||
string group_name = 1;
|
||||
}
|
||||
|
||||
message DestroyResponse {
|
||||
}
|
||||
239
crates/dcs-grpc/protos/dcs/hook/v0/hook.proto
Normal file
239
crates/dcs-grpc/protos/dcs/hook/v0/hook.proto
Normal file
@@ -0,0 +1,239 @@
|
||||
syntax = "proto3";
|
||||
package dcs.hook.v0;
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Hook";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/hook";
|
||||
|
||||
// APis that are part of the hook environment
|
||||
service HookService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getMissionName
|
||||
rpc GetMissionName(GetMissionNameRequest) returns (GetMissionNameResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getMissionFilename
|
||||
rpc GetMissionFilename(GetMissionFilenameRequest)
|
||||
returns (GetMissionFilenameResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getMissionDescription
|
||||
rpc GetMissionDescription(GetMissionDescriptionRequest)
|
||||
returns (GetMissionDescriptionResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getPause
|
||||
rpc GetPaused(GetPausedRequest) returns (GetPausedResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_setPause
|
||||
rpc SetPaused(SetPausedRequest) returns (SetPausedResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_stopMission
|
||||
rpc StopMission(StopMissionRequest) returns (StopMissionResponse) {}
|
||||
|
||||
// Reload the currently running mission
|
||||
rpc ReloadCurrentMission(ReloadCurrentMissionRequest)
|
||||
returns (ReloadCurrentMissionResponse) {}
|
||||
|
||||
// Load the next mission in the server mission list. Note that it does
|
||||
// not loop back to the first mission once the end of the mission list
|
||||
// has been reached
|
||||
rpc LoadNextMission(LoadNextMissionRequest)
|
||||
returns (LoadNextMissionResponse) {}
|
||||
|
||||
// Load a specific mission file. This does not need to be in the mission
|
||||
// list.
|
||||
rpc LoadMission(LoadMissionRequest)
|
||||
returns (LoadMissionResponse) {}
|
||||
|
||||
// Evaluate some Lua inside of the hook environment and return the result as a
|
||||
// JSON string. Disabled by default.
|
||||
rpc Eval(EvalRequest) returns (EvalResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_exitProcess
|
||||
rpc ExitProcess(ExitProcessRequest) returns (ExitProcessResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_isMultiplayer
|
||||
rpc IsMultiplayer(IsMultiplayerRequest) returns (IsMultiplayerResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_isServer
|
||||
rpc IsServer(IsServerRequest) returns (IsServerResponse) {}
|
||||
|
||||
// Bans a player that is currently connected to the server
|
||||
rpc BanPlayer(BanPlayerRequest) returns (BanPlayerResponse) {}
|
||||
|
||||
// Unbans a player via their globally unique ID
|
||||
rpc UnbanPlayer(UnbanPlayerRequest) returns (UnbanPlayerResponse) {}
|
||||
|
||||
// Get a list of all the banned players
|
||||
rpc GetBannedPlayers(GetBannedPlayersRequest)
|
||||
returns (GetBannedPlayersResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getUnitType
|
||||
rpc GetUnitType(GetUnitTypeRequest) returns (GetUnitTypeResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getRealTime
|
||||
rpc GetRealTime(GetRealTimeRequest) returns (GetRealTimeResponse) {}
|
||||
|
||||
// Get a count of ballistics objects
|
||||
rpc GetBallisticsCount(GetBallisticsCountRequest)
|
||||
returns (GetBallisticsCountResponse) {}
|
||||
}
|
||||
|
||||
message GetMissionNameRequest {
|
||||
}
|
||||
|
||||
message GetMissionNameResponse {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetMissionFilenameRequest {
|
||||
}
|
||||
|
||||
message GetMissionFilenameResponse {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetMissionDescriptionRequest {
|
||||
}
|
||||
|
||||
message GetMissionDescriptionResponse {
|
||||
string description = 1;
|
||||
}
|
||||
|
||||
message GetPausedRequest {
|
||||
}
|
||||
|
||||
message GetPausedResponse {
|
||||
bool paused = 1;
|
||||
}
|
||||
|
||||
message SetPausedRequest {
|
||||
bool paused = 1;
|
||||
}
|
||||
|
||||
message SetPausedResponse {
|
||||
}
|
||||
|
||||
message ReloadCurrentMissionRequest {
|
||||
}
|
||||
|
||||
message ReloadCurrentMissionResponse {
|
||||
}
|
||||
|
||||
message LoadNextMissionRequest {
|
||||
}
|
||||
|
||||
message LoadNextMissionResponse {
|
||||
// Was the next mission successfully loaded. SHOULD return false when the
|
||||
// end of the mission list has been reached but DCS appears to always
|
||||
// return true
|
||||
bool loaded = 1;
|
||||
}
|
||||
|
||||
message LoadMissionRequest {
|
||||
// The full path to the .miz file to be loaded
|
||||
string file_name = 1;
|
||||
}
|
||||
|
||||
message LoadMissionResponse {
|
||||
}
|
||||
|
||||
message StopMissionRequest {
|
||||
}
|
||||
|
||||
message StopMissionResponse {
|
||||
}
|
||||
|
||||
message EvalRequest {
|
||||
string lua = 1;
|
||||
}
|
||||
|
||||
message EvalResponse {
|
||||
string json = 1;
|
||||
}
|
||||
|
||||
message ExitProcessRequest {
|
||||
}
|
||||
|
||||
message ExitProcessResponse {
|
||||
}
|
||||
|
||||
message IsMultiplayerRequest {
|
||||
}
|
||||
|
||||
message IsMultiplayerResponse {
|
||||
bool multiplayer = 1;
|
||||
}
|
||||
|
||||
message IsServerRequest {
|
||||
}
|
||||
|
||||
message IsServerResponse {
|
||||
bool server = 1;
|
||||
}
|
||||
|
||||
message BanPlayerRequest {
|
||||
// The session ID of the player
|
||||
uint32 id = 1;
|
||||
// The period of the ban in seconds
|
||||
uint32 period = 2;
|
||||
// The reason for the ban
|
||||
string reason = 3;
|
||||
}
|
||||
|
||||
message BanPlayerResponse {
|
||||
// Was the player successfully banned
|
||||
bool banned = 1;
|
||||
}
|
||||
|
||||
message UnbanPlayerRequest {
|
||||
// The globally unique ID of the player
|
||||
string ucid = 1;
|
||||
}
|
||||
|
||||
message UnbanPlayerResponse {
|
||||
// Was the player successfully unbanned
|
||||
bool unbanned = 1;
|
||||
}
|
||||
|
||||
message GetBannedPlayersRequest {
|
||||
}
|
||||
|
||||
message GetBannedPlayersResponse {
|
||||
repeated BanDetails bans = 1;
|
||||
}
|
||||
|
||||
message BanDetails {
|
||||
// The globally unique ID of the player
|
||||
string ucid = 1;
|
||||
// The IP address the user had when they were banned
|
||||
string ip_address = 2;
|
||||
// The Name of the player at the time of the ban
|
||||
string player_name = 3;
|
||||
// The reason given for the ban
|
||||
string reason = 4;
|
||||
// When the ban was issued in unixtime
|
||||
uint64 banned_from = 5;
|
||||
// When the ban will expire in unixtime
|
||||
uint64 banned_until = 6;
|
||||
}
|
||||
|
||||
message GetUnitTypeRequest {
|
||||
// The slot or unit ID of the unit to retrieve the type of
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetUnitTypeResponse {
|
||||
// Type of unit (e.g. "F-14B")
|
||||
string type = 1;
|
||||
}
|
||||
|
||||
message GetRealTimeRequest {
|
||||
}
|
||||
|
||||
message GetRealTimeResponse {
|
||||
// The current time in a mission relative to the DCS start time
|
||||
double time = 1;
|
||||
}
|
||||
|
||||
message GetBallisticsCountRequest {
|
||||
}
|
||||
|
||||
message GetBallisticsCountResponse {
|
||||
uint32 count = 1;
|
||||
}
|
||||
780
crates/dcs-grpc/protos/dcs/mission/v0/mission.proto
Normal file
780
crates/dcs-grpc/protos/dcs/mission/v0/mission.proto
Normal file
@@ -0,0 +1,780 @@
|
||||
syntax = "proto3";
|
||||
package dcs.mission.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Mission";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/mission";
|
||||
|
||||
// the "path" field in mission command requests and responses is a repeated
|
||||
// string however "paths" doesn't make sense. therefore we will disable
|
||||
// the linter pluralization checks for this file.
|
||||
// protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
|
||||
|
||||
// Contains the streaming APIs that streaming information out of the DCS server.
|
||||
service MissionService {
|
||||
// Streams DCS game generated Events.
|
||||
// See https://wiki.hoggitworld.com/view/Category:Events
|
||||
rpc StreamEvents(StreamEventsRequest) returns (stream StreamEventsResponse) {}
|
||||
|
||||
// Streams unit updates
|
||||
// Provides similar functionality as Tacview but at a much lower update rate
|
||||
// so puts less load on the server. Suitable for things like online maps but
|
||||
// not as a Tacview replacement.
|
||||
rpc StreamUnits(StreamUnitsRequest) returns (stream StreamUnitsResponse) {}
|
||||
|
||||
// Returns the mission's in-game starttime as an ISO 8601 formatted datetime
|
||||
// string.
|
||||
rpc GetScenarioStartTime(GetScenarioStartTimeRequest)
|
||||
returns (GetScenarioStartTimeResponse) {}
|
||||
|
||||
// Returns the mission's in-game current time as an ISO 8601 formatted
|
||||
// datetime string.
|
||||
rpc GetScenarioCurrentTime(GetScenarioCurrentTimeRequest)
|
||||
returns (GetScenarioCurrentTimeResponse) {}
|
||||
|
||||
// Adds a new mission command
|
||||
// See https://wiki.hoggitworld.com/view/DCS_func_addCommand
|
||||
rpc AddMissionCommand(AddMissionCommandRequest)
|
||||
returns (AddMissionCommandResponse) {}
|
||||
|
||||
// Adds a new command sub menu
|
||||
// See https://wiki.hoggitworld.com/view/DCS_func_addSubMenu
|
||||
rpc AddMissionCommandSubMenu(AddMissionCommandSubMenuRequest)
|
||||
returns (AddMissionCommandSubMenuResponse) {}
|
||||
|
||||
// Removes a registered mission command.
|
||||
// See https://wiki.hoggitworld.com/view/DCS_func_removeItem
|
||||
rpc RemoveMissionCommandItem(RemoveMissionCommandItemRequest)
|
||||
returns (RemoveMissionCommandItemResponse) {}
|
||||
|
||||
// Adds a new coalition command
|
||||
// See https://wiki.hoggitworld.com/view/DCS_func_addCommandForCoalition
|
||||
rpc AddCoalitionCommand(AddCoalitionCommandRequest)
|
||||
returns (AddCoalitionCommandResponse) {}
|
||||
|
||||
// Adds a new coalition command sub menu
|
||||
// See https://wiki.hoggitworld.com/view/DCS_func_addSubMenuForCoalition
|
||||
rpc AddCoalitionCommandSubMenu(AddCoalitionCommandSubMenuRequest)
|
||||
returns (AddCoalitionCommandSubMenuResponse) {}
|
||||
|
||||
// Removes a registered coalition command.
|
||||
// See https://wiki.hoggitworld.com/view/DCS_func_removeItemForCoalition
|
||||
rpc RemoveCoalitionCommandItem(RemoveCoalitionCommandItemRequest)
|
||||
returns (RemoveCoalitionCommandItemResponse) {}
|
||||
|
||||
// Adds a new group command
|
||||
// See https://wiki.hoggitworld.com/view/DCS_func_addCommandForGroup
|
||||
rpc AddGroupCommand(AddGroupCommandRequest)
|
||||
returns (AddGroupCommandResponse) {}
|
||||
|
||||
// Adds a new group command sub menu
|
||||
// See https://wiki.hoggitworld.com/view/DCS_func_addSubMenuForGroup
|
||||
rpc AddGroupCommandSubMenu(AddGroupCommandSubMenuRequest)
|
||||
returns (AddGroupCommandSubMenuResponse) {}
|
||||
|
||||
// Removes a group coalition command.
|
||||
// See https://wiki.hoggitworld.com/view/DCS_func_removeItemForGroup
|
||||
rpc RemoveGroupCommandItem(RemoveGroupCommandItemRequest)
|
||||
returns (RemoveGroupCommandItemResponse) {}
|
||||
|
||||
// Returns an ID for the current session.
|
||||
// The ID will change upon mission change or server restart.
|
||||
rpc GetSessionId(GetSessionIdRequest)
|
||||
returns (GetSessionIdResponse) {}
|
||||
}
|
||||
|
||||
message StreamEventsRequest {
|
||||
}
|
||||
|
||||
// The DCS Event information. Contains event information and a timestamp.
|
||||
message StreamEventsResponse {
|
||||
// Occurs when a unit fires a weapon (but no machine gun- or autocannon-based
|
||||
// weapons - those are handled by [ShootingStartEvent]).
|
||||
message ShotEvent {
|
||||
// The object that fired the weapon.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The weapon that has been fired.
|
||||
dcs.common.v0.Weapon weapon = 2;
|
||||
}
|
||||
|
||||
// Occurs when an object is hit by a weapon.
|
||||
message HitEvent {
|
||||
// The object that fired the weapon. Not set when for example fyling an
|
||||
// aircraft into a building (building will be the target and weapon_name the
|
||||
// name of the aircraft).
|
||||
optional dcs.common.v0.Initiator initiator = 1;
|
||||
// The weapon that the target has been hit with.
|
||||
dcs.common.v0.Weapon weapon = 2;
|
||||
// The object that has been hit.
|
||||
dcs.common.v0.Target target = 3;
|
||||
// The weapon the target got hit by.
|
||||
optional string weapon_name = 4;
|
||||
}
|
||||
|
||||
// Occurs when an aircraft takes off from an airbase, farp, or ship.
|
||||
message TakeoffEvent {
|
||||
// The object that took off.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The airbase, farp or ship the unit took off from.
|
||||
dcs.common.v0.Airbase place = 2;
|
||||
}
|
||||
|
||||
// Occurs when an aircraft lands at an airbase, farp or ship.
|
||||
message LandEvent {
|
||||
// The object that landed.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The airbase, farp or ship the unit landed at.
|
||||
dcs.common.v0.Airbase place = 2;
|
||||
}
|
||||
|
||||
// Occurs when an aircraft crashes into the ground and is completely
|
||||
// destroyed.
|
||||
message CrashEvent {
|
||||
// The object that crashed.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
}
|
||||
|
||||
// Occurs when a pilot ejects from its aircraft.
|
||||
message EjectionEvent {
|
||||
// The unit a pilot ejected from.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The ejection seat.
|
||||
dcs.common.v0.Target target = 3;
|
||||
}
|
||||
|
||||
// Occurs when an aircraft connects with a tanker and begins taking on fuel.
|
||||
message RefuelingEvent {
|
||||
// The object that is receiving fuel.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
}
|
||||
|
||||
// Occurs when an object is completely destroyed.
|
||||
message DeadEvent {
|
||||
// The object that has been destroyed.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
}
|
||||
|
||||
// Occurs when a pilot of an aircraft is killed. Can occur either if the
|
||||
// player is alive and crashes (in this case both this and the [CrashEvent]
|
||||
// event will be fired) or if a weapon kills the pilot without completely
|
||||
// destroying the plane.
|
||||
message PilotDeadEvent {
|
||||
// The unit the pilot has died in.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
}
|
||||
|
||||
// Occurs when a ground unit captures either an airbase or a farp.
|
||||
message BaseCaptureEvent {
|
||||
// The object that captured the base.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The airbase that was captured, can be a FARP or Airbase
|
||||
dcs.common.v0.Airbase place = 2;
|
||||
}
|
||||
|
||||
// Occurs when the mission starts.
|
||||
message MissionStartEvent {
|
||||
}
|
||||
|
||||
// Occurs when the mission stops.
|
||||
message MissionEndEvent {
|
||||
}
|
||||
|
||||
// Occurs when an aircraft is finished taking fuel.
|
||||
message RefuelingStopEvent {
|
||||
// he unit that was receiving fuel.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
}
|
||||
|
||||
// Occurs when any object is spawned into the mission.
|
||||
message BirthEvent {
|
||||
// The object that was spawned.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The airbase, farp or ship the unit took off from.
|
||||
optional dcs.common.v0.Airbase place = 2;
|
||||
}
|
||||
|
||||
// Occurs e.g. when a player controlled aircraft blacks out.
|
||||
message HumanFailureEvent {
|
||||
// The unit the system failure occurred in.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
}
|
||||
|
||||
// Occurs when a system on an aircraft fails. This can be due to damage or due
|
||||
// to random failures set up in the mission editor.
|
||||
message DetailedFailureEvent {
|
||||
// The target the failure occurred for.
|
||||
dcs.common.v0.Target target = 1;
|
||||
}
|
||||
|
||||
// Occurs when any aircraft starts its engines.
|
||||
message EngineStartupEvent {
|
||||
// The object that starts its engines.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The airbase, farp or ship the unit started their engine at.
|
||||
dcs.common.v0.Airbase place = 2;
|
||||
}
|
||||
|
||||
message EngineShutdownEvent {
|
||||
// Occurs when any aircraft shuts down its engines.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The airbase, farp or ship the unit shut down their engine at.
|
||||
dcs.common.v0.Airbase place = 2;
|
||||
}
|
||||
|
||||
// Occurs when a player takes direct control of a unit.
|
||||
message PlayerEnterUnitEvent {
|
||||
// The unit the player took control of.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
}
|
||||
|
||||
// Occurs when a player relieves direct control of a unit.
|
||||
message PlayerLeaveUnitEvent {
|
||||
// The unit the player relieves control of.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
}
|
||||
|
||||
// Occurs when a unit begins firing a machine gun- or autocannon-based weapon
|
||||
// (weapons with a high rate of fire). Other weapons are handled by
|
||||
// [ShotEvent].
|
||||
message ShootingStartEvent {
|
||||
// The object that started firing.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The name of the shoot weapon.
|
||||
string weapon_name = 2;
|
||||
}
|
||||
|
||||
// Occurs when a unit stops firing a machine gun- or autocannon-based weapon.
|
||||
// Event will always correspond with a [ShootingStartEvent] event.
|
||||
message ShootingEndEvent {
|
||||
// The object that was shooting and has no stopped firing.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The name of the shoot weapon.
|
||||
string weapon_name = 2;
|
||||
}
|
||||
|
||||
// Occurs when marks get added to the mission by players or scripting
|
||||
// functions.
|
||||
message MarkAddEvent {
|
||||
// The object that added the mark.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
oneof visibility {
|
||||
// The group the mark's visibility is restricted for.
|
||||
uint64 group_id = 2;
|
||||
// The coalition the mark's visibility is restricted for.
|
||||
dcs.common.v0.Coalition coalition = 3;
|
||||
}
|
||||
// The mark's id.
|
||||
uint32 id = 4;
|
||||
// The position the mark has been added at.
|
||||
dcs.common.v0.Position position = 5;
|
||||
// The mark's label.
|
||||
string text = 6;
|
||||
}
|
||||
|
||||
// Occurs when marks got changed.
|
||||
message MarkChangeEvent {
|
||||
// The object that changed the mark.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
oneof visibility {
|
||||
// The group the mark's visibility is restricted for.
|
||||
uint64 group_id = 2;
|
||||
// The coalition the mark's visibility is restricted for.
|
||||
dcs.common.v0.Coalition coalition = 3;
|
||||
}
|
||||
// The mark's id.
|
||||
uint32 id = 4;
|
||||
// The position of the changed mark.
|
||||
dcs.common.v0.Position position = 5;
|
||||
// The mark's label.
|
||||
string text = 6;
|
||||
}
|
||||
|
||||
// Occurs when marks get removed.
|
||||
message MarkRemoveEvent {
|
||||
// The object that removed the mark.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
oneof visibility {
|
||||
// The group the mark's visibility is restricted for.
|
||||
uint64 group_id = 2;
|
||||
// The coalition the mark's visibility is restricted for.
|
||||
dcs.common.v0.Coalition coalition = 3;
|
||||
}
|
||||
// The mark's id.
|
||||
uint32 id = 4;
|
||||
// The position the mark has been removed from.
|
||||
dcs.common.v0.Position position = 5;
|
||||
// The mark's label.
|
||||
string text = 6;
|
||||
}
|
||||
|
||||
// Occurs when an object is killed by a weapon.
|
||||
message KillEvent {
|
||||
// The object that fired the weapon.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The weapon that the target has been killed with.
|
||||
dcs.common.v0.Weapon weapon = 2;
|
||||
// The object that has been killed.
|
||||
dcs.common.v0.Target target = 3;
|
||||
// The name of the weapon that killed the target (exists instead of weapon
|
||||
// for weapons that trigger the shooting start and end events).
|
||||
optional string weapon_name = 4;
|
||||
}
|
||||
|
||||
// A score change (doesn't contain any useful information)
|
||||
message ScoreEvent {
|
||||
}
|
||||
|
||||
// A unit got destroyed.
|
||||
message UnitLostEvent {
|
||||
// The object that got destroyed weapon.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
}
|
||||
|
||||
// A pilot detached from their ejection seat.
|
||||
message LandingAfterEjectionEvent {
|
||||
// The ejected pilot.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The position the pilot landed at.
|
||||
dcs.common.v0.Position place = 2;
|
||||
}
|
||||
|
||||
// A pilot detached from their ejection seat.
|
||||
message DiscardChairAfterEjectionEvent {
|
||||
// The ejection seat.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The pilot.
|
||||
dcs.common.v0.Target target = 2;
|
||||
}
|
||||
|
||||
// Fired for each payload of an aircraft spawened midair.
|
||||
message WeaponAddEvent {
|
||||
// The object that got spawned.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The name of the payload.
|
||||
string weapon_name = 2;
|
||||
}
|
||||
|
||||
// Occurs when an aircraft receives an LSO rating after recovering on an
|
||||
// aircraft carrier.
|
||||
message LandingQualityMarkEvent {
|
||||
// The aircraft that received the rating.
|
||||
dcs.common.v0.Initiator initiator = 1;
|
||||
// The rating.
|
||||
string comment = 2;
|
||||
// The ship the unit landed at.
|
||||
dcs.common.v0.Airbase place = 3;
|
||||
}
|
||||
|
||||
// Occurs when a chat message is sent on the server
|
||||
message PlayerSendChatEvent {
|
||||
// The player's id in the current server session.
|
||||
uint32 player_id = 1;
|
||||
// what was typed
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
// fired when the player changes across to a slot
|
||||
message PlayerChangeSlotEvent {
|
||||
// The player's id in the current server session.
|
||||
uint32 player_id = 1;
|
||||
// The slot's coalition
|
||||
dcs.common.v0.Coalition coalition = 2;
|
||||
// The slot's identifier
|
||||
string slot_id = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a player connected to the server.
|
||||
*/
|
||||
message ConnectEvent {
|
||||
// The player's IP and port.
|
||||
string addr = 1;
|
||||
// The name of the player.
|
||||
string name = 2;
|
||||
// The player's unique client identifier (used to ban a player).
|
||||
string ucid = 3;
|
||||
// The player's id in the current server session
|
||||
// (used to for name/slot/... changes).
|
||||
uint32 id = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* The reason a player disconnected for.
|
||||
*/
|
||||
enum DisconnectReason {
|
||||
DISCONNECT_REASON_UNSPECIFIED = 0;
|
||||
DISCONNECT_REASON_THATS_OKAY = 1;
|
||||
DISCONNECT_REASON_INVALID_ADDRESS = 2;
|
||||
DISCONNECT_REASON_CONNECT_FAILED = 3;
|
||||
DISCONNECT_REASON_WRONG_VERSION = 4;
|
||||
DISCONNECT_REASON_PROTOCOL_ERROR = 5;
|
||||
DISCONNECT_REASON_TIMEOUT = 6;
|
||||
DISCONNECT_REASON_INVALID_PASSWORD = 101;
|
||||
DISCONNECT_REASON_BANNED = 102;
|
||||
DISCONNECT_REASON_BAD_CALLSIGN = 103;
|
||||
DISCONNECT_REASON_TAINTED_CLIENT = 104;
|
||||
DISCONNECT_REASON_KICKED = 105;
|
||||
DISCONNECT_REASON_REFUSED = 106;
|
||||
DISCONNECT_REASON_DENIED_TRIAL_ONLY = 107;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a player disconnected from the server
|
||||
* (not fired for the server's player).
|
||||
*/
|
||||
message DisconnectEvent {
|
||||
// The player's id in the current server session.
|
||||
uint32 id = 1;
|
||||
// The reason a player disconnected for.
|
||||
DisconnectReason reason = 2;
|
||||
}
|
||||
|
||||
message MissionCommandEvent {
|
||||
// A struct containing details of the command that was run by a player
|
||||
google.protobuf.Struct details = 1;
|
||||
}
|
||||
|
||||
message CoalitionCommandEvent {
|
||||
// The coalition of the player who ran the command
|
||||
dcs.common.v0.Coalition coalition = 1;
|
||||
// A struct containing details of the command that was run by a player
|
||||
google.protobuf.Struct details = 2;
|
||||
}
|
||||
|
||||
message GroupCommandEvent {
|
||||
// Details of the group to which the player who ran the command is a unit of
|
||||
dcs.common.v0.Group group = 1;
|
||||
// A struct containing details of the command that was run by a player
|
||||
google.protobuf.Struct details = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired every second containing simulation FPS information since the previous
|
||||
* event.
|
||||
*/
|
||||
message SimulationFpsEvent {
|
||||
// The average FPS since the last event.
|
||||
double average = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired for every TTS request that contains the `text_plain` field, for other
|
||||
* clients to use e.g. for accessibility use-cases.
|
||||
*/
|
||||
message TtsEvent {
|
||||
// The plain text that got transmitted.
|
||||
string text = 1;
|
||||
|
||||
// The radio frequency in Hz the transmission got send to.
|
||||
uint64 frequency = 2;
|
||||
|
||||
// The coalition of the transmission.
|
||||
dcs.common.v0.Coalition coalition = 3;
|
||||
|
||||
// Custom name of the SRS client used for the transmission.
|
||||
optional string srs_client_name = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired every time a player occuping a unit connects to a frequency on SRS.
|
||||
*/
|
||||
message SrsConnectEvent {
|
||||
// The unit that connected to a frequency in SRS.
|
||||
dcs.common.v0.Unit unit = 1;
|
||||
|
||||
// The radio frequency in Hz the unit connected to.
|
||||
uint64 frequency = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired every time a player occuping a unit disconnects from a frequency on
|
||||
* SRS. It is not fired when the player leaves the unit or the unit dies.
|
||||
*/
|
||||
message SrsDisconnectEvent {
|
||||
// The unit that disconnected from a frequency in SRS.
|
||||
dcs.common.v0.Unit unit = 1;
|
||||
|
||||
// The radio frequency in Hz the unit disconnected from.
|
||||
uint64 frequency = 2;
|
||||
}
|
||||
|
||||
// The event's mission time.
|
||||
double time = 1;
|
||||
oneof event {
|
||||
ShotEvent shot = 4;
|
||||
HitEvent hit = 5;
|
||||
TakeoffEvent takeoff = 6;
|
||||
LandEvent land = 7;
|
||||
CrashEvent crash = 8;
|
||||
EjectionEvent ejection = 9;
|
||||
RefuelingEvent refueling = 10;
|
||||
DeadEvent dead = 11;
|
||||
PilotDeadEvent pilot_dead = 12;
|
||||
BaseCaptureEvent base_capture = 13;
|
||||
MissionStartEvent mission_start = 14;
|
||||
MissionEndEvent mission_end = 15;
|
||||
// @exclude 16 reserved for S_EVENT_TOOK_CONTROL
|
||||
RefuelingStopEvent refueling_stop = 17;
|
||||
BirthEvent birth = 18;
|
||||
HumanFailureEvent human_failure = 19;
|
||||
DetailedFailureEvent detailed_failure = 20;
|
||||
EngineStartupEvent engine_startup = 21;
|
||||
EngineShutdownEvent engine_shutdown = 22;
|
||||
PlayerEnterUnitEvent player_enter_unit = 23;
|
||||
PlayerLeaveUnitEvent player_leave_unit = 24;
|
||||
// @exclude 25 reserved for S_EVENT_PLAYER_COMMENT
|
||||
ShootingStartEvent shooting_start = 26;
|
||||
ShootingEndEvent shooting_end = 27;
|
||||
MarkAddEvent mark_add = 28;
|
||||
MarkChangeEvent mark_change = 29;
|
||||
MarkRemoveEvent mark_remove = 30;
|
||||
KillEvent kill = 31;
|
||||
ScoreEvent score = 32;
|
||||
UnitLostEvent unit_lost = 33;
|
||||
LandingAfterEjectionEvent landing_after_ejection = 34;
|
||||
// @exclude 35 reserved for S_EVENT_PARATROOPER_LENDING
|
||||
DiscardChairAfterEjectionEvent discard_chair_after_ejection = 36;
|
||||
WeaponAddEvent weapon_add = 37;
|
||||
// @exclude 38 reserved for S_EVENT_TRIGGER_ZONE
|
||||
LandingQualityMarkEvent landing_quality_mark = 39;
|
||||
// @exclude 40 reserved for S_EVENT_BDA
|
||||
|
||||
// The following events are additions on top of DCS's own event enum,
|
||||
// which is why they start at 8192 to give DCS plenty of space for
|
||||
// new built-in events.
|
||||
ConnectEvent connect = 8192;
|
||||
DisconnectEvent disconnect = 8193;
|
||||
PlayerSendChatEvent player_send_chat = 8194;
|
||||
PlayerChangeSlotEvent player_change_slot = 8195;
|
||||
MissionCommandEvent mission_command = 8196;
|
||||
CoalitionCommandEvent coalition_command = 8197;
|
||||
GroupCommandEvent group_command = 8198;
|
||||
SimulationFpsEvent simulation_fps = 8199;
|
||||
TtsEvent tts = 8200;
|
||||
SrsConnectEvent srs_connect = 8201;
|
||||
SrsDisconnectEvent srs_disconnect = 8202;
|
||||
}
|
||||
}
|
||||
|
||||
message StreamUnitsRequest {
|
||||
// The poll rate in seconds at which the gRPC server communicates with the DCS
|
||||
// mission to retrieve the latest unit positions. The lower the `poll_rate`
|
||||
// the higher the amount of requests send to to the DCS mission. Default: 5
|
||||
optional uint32 poll_rate = 1;
|
||||
|
||||
// The maximum backoff in seconds which the gRPC postpones polling units that
|
||||
// haven't moved recently. This is an optimization to dynamically reduce the
|
||||
// poll rate for stationary units. Set it to the same value as `poll_rate` to
|
||||
// disable the backoff. Default: 30
|
||||
optional uint32 max_backoff = 2;
|
||||
|
||||
// The type of the unit to stream movements. Different categories of units
|
||||
// would move at different speeds, which allows the stream to be configured
|
||||
// with the appropriate polling rates. `GROUP_CATEGORY_UNSPECIFIED` would
|
||||
// return all the units.
|
||||
dcs.common.v0.GroupCategory category = 3;
|
||||
}
|
||||
|
||||
message StreamUnitsResponse {
|
||||
message UnitGone {
|
||||
uint32 id = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
double time = 1;
|
||||
|
||||
oneof update {
|
||||
// The unit is either new or its position or attitude changed.
|
||||
dcs.common.v0.Unit unit = 2;
|
||||
|
||||
// The unit does not exist anymore.
|
||||
UnitGone gone = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message GetScenarioStartTimeRequest {
|
||||
}
|
||||
|
||||
message GetScenarioStartTimeResponse {
|
||||
string datetime = 1;
|
||||
}
|
||||
|
||||
message GetScenarioCurrentTimeRequest {
|
||||
}
|
||||
|
||||
message GetScenarioCurrentTimeResponse {
|
||||
string datetime = 1;
|
||||
}
|
||||
|
||||
// MISSION COMMANDS
|
||||
// GLOBAL
|
||||
|
||||
// Adds an F10 radio command visible to all players in all coalitions.
|
||||
// When the player activates the command then a `missionCommand` event will be
|
||||
// emitted to all connected DCS-gRPC clients for processing as they see fit.
|
||||
message AddMissionCommandRequest {
|
||||
// The name of the command that is displayed to the player.
|
||||
// It will form the last entry in the returned path.
|
||||
string name = 1;
|
||||
// The menu path the command will appear under. This can be empty if you want
|
||||
// the command to be on the first level under the F10 menu. This path must
|
||||
// already have been created.
|
||||
repeated string path = 2;
|
||||
// A struct containing data that will be included in the emitted event to the
|
||||
// DCS-gRPC clients
|
||||
google.protobuf.Struct details = 3;
|
||||
}
|
||||
|
||||
message AddMissionCommandResponse {
|
||||
// The full path to the command, including the command name. Use this path to
|
||||
// delete the command.
|
||||
repeated string path = 1;
|
||||
}
|
||||
|
||||
message AddMissionCommandSubMenuRequest {
|
||||
// The name of the submenu that is displayed to the player.
|
||||
// It will form the last entry in the returned path.
|
||||
string name = 1;
|
||||
// The menu path the submenu will appear under. This can be empty if you want
|
||||
// the submenu to be on the first level under the F10 menu. This path must
|
||||
// already have been created using this command. you cannot create a nested
|
||||
// submenu tree in one command.
|
||||
repeated string path = 2;
|
||||
}
|
||||
|
||||
message AddMissionCommandSubMenuResponse {
|
||||
// The full path to the submenu, including the submenu name. Use this path to
|
||||
// add another submenu or command underneath it or delete the submenu.
|
||||
repeated string path = 1;
|
||||
}
|
||||
|
||||
message RemoveMissionCommandItemRequest {
|
||||
// The full path to the menu item, which can be a submenu or a command, to be
|
||||
// removed. Deleting a menu item will delete all children it may have.
|
||||
repeated string path = 1;
|
||||
}
|
||||
|
||||
message RemoveMissionCommandItemResponse {
|
||||
}
|
||||
|
||||
// COALITION
|
||||
|
||||
// Adds an F10 radio command visible to all players in the specified coalition.
|
||||
// When the player activates the command then a `coalitionCommand` event will
|
||||
// be emitted to all connected DCS-gRPC clients for processing as they see fit.
|
||||
// The emitted event will include the coalition.
|
||||
message AddCoalitionCommandRequest {
|
||||
// The coalition whose players will be able to see and run the command
|
||||
dcs.common.v0.Coalition coalition = 1;
|
||||
// The name of the command that is displayed to the player.
|
||||
// It will form the last entry in the returned path.
|
||||
string name = 2;
|
||||
// The menu path the command will appear under. This can be empty if you want
|
||||
// the command to be on the first level under the F10 menu. This path must
|
||||
// already have been created.
|
||||
repeated string path = 3;
|
||||
// A struct containing data that will be included in the emitted event to the
|
||||
// DCS-gRPC clients
|
||||
google.protobuf.Struct details = 4;
|
||||
}
|
||||
|
||||
message AddCoalitionCommandResponse {
|
||||
// The full path to the command, including the command name. Use this path to
|
||||
// delete the command.
|
||||
repeated string path = 1;
|
||||
}
|
||||
|
||||
message AddCoalitionCommandSubMenuRequest {
|
||||
// The coalition whose players will be able to see the submenu
|
||||
dcs.common.v0.Coalition coalition = 1;
|
||||
// The name of the submenu that is displayed to the player.
|
||||
// It will form the last entry in the returned path.
|
||||
string name = 2;
|
||||
// The menu path the submenu will appear under. This can be empty if you want
|
||||
// the submenu to be on the first level under the F10 menu. This path must
|
||||
// already have been created using this command. you cannot create a nested
|
||||
// submenu tree in one command.
|
||||
repeated string path = 3;
|
||||
}
|
||||
|
||||
message AddCoalitionCommandSubMenuResponse {
|
||||
// The full path to the submenu, including the submenu name. Use this path to
|
||||
// add another submenu or command underneath it or delete the submenu.
|
||||
repeated string path = 1;
|
||||
}
|
||||
|
||||
message RemoveCoalitionCommandItemRequest {
|
||||
// The coalition whose players will have the menu item removed
|
||||
dcs.common.v0.Coalition coalition = 1;
|
||||
// The full path to the menu item, which can be a submenu or a command, to be
|
||||
// removed. Deleting a menu item will delete all children it may have.
|
||||
repeated string path = 2;
|
||||
}
|
||||
|
||||
message RemoveCoalitionCommandItemResponse {
|
||||
}
|
||||
|
||||
// GROUP
|
||||
|
||||
// Adds an F10 radio command visible to all players in the specified group.
|
||||
// When the player activates the command then a `groupCommand` event will
|
||||
// be emitted to all connected DCS-gRPC clients for processing as they see fit.
|
||||
// The emitted event will include the group name.
|
||||
message AddGroupCommandRequest {
|
||||
// The name of the group whose players will be able to see and execute the
|
||||
// command. TODO (Figure out if this persists across spawns)
|
||||
string group_name = 1;
|
||||
// The name of the command that is displayed to the player.
|
||||
// It will form the last entry in the returned path.
|
||||
string name = 2;
|
||||
// The menu path the command will appear under. This can be empty if you want
|
||||
// the command to be on the first level under the F10 menu. This path must
|
||||
// already have been created.
|
||||
repeated string path = 3;
|
||||
// A struct containing data that will be included in the emitted event to the
|
||||
// DCS-gRPC clients
|
||||
google.protobuf.Struct details = 4;
|
||||
}
|
||||
|
||||
message AddGroupCommandResponse {
|
||||
// The full path to the command, including the command name. Use this path to
|
||||
// delete the command.
|
||||
repeated string path = 1;
|
||||
}
|
||||
|
||||
message AddGroupCommandSubMenuRequest {
|
||||
// The name of the group whose players will be able to see the submenu
|
||||
string group_name = 1;
|
||||
// The name of the submenu that is displayed to the player.
|
||||
// It will form the last entry in the returned path.
|
||||
string name = 2;
|
||||
// The menu path the submenu will appear under. This can be empty if you want
|
||||
// the submenu to be on the first level under the F10 menu. This path must
|
||||
// already have been created using this command. you cannot create a nested
|
||||
// submenu tree in one command.
|
||||
repeated string path = 3;
|
||||
}
|
||||
|
||||
message AddGroupCommandSubMenuResponse {
|
||||
// The full path to the submenu, including the submenu name. Use this path to
|
||||
// add another submenu or command underneath it or delete the submenu.
|
||||
repeated string path = 1;
|
||||
}
|
||||
|
||||
message RemoveGroupCommandItemRequest {
|
||||
// The group whose players will have the menu item removed
|
||||
string group_name = 1;
|
||||
// The full path to the menu item, which can be a submenu or a command, to be
|
||||
// removed. Deleting a menu item will delete all children it may have.
|
||||
repeated string path = 2;
|
||||
}
|
||||
|
||||
message RemoveGroupCommandItemResponse {
|
||||
}
|
||||
|
||||
message GetSessionIdRequest {
|
||||
}
|
||||
|
||||
message GetSessionIdResponse {
|
||||
int64 session_id = 1;
|
||||
}
|
||||
93
crates/dcs-grpc/protos/dcs/net/v0/net.proto
Normal file
93
crates/dcs-grpc/protos/dcs/net/v0/net.proto
Normal file
@@ -0,0 +1,93 @@
|
||||
syntax = "proto3";
|
||||
package dcs.net.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Net";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/net";
|
||||
|
||||
service NetService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_send_chat_to
|
||||
rpc SendChatTo(SendChatToRequest) returns (SendChatToResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_send_chat
|
||||
rpc SendChat(SendChatRequest) returns (SendChatResponse) {}
|
||||
|
||||
// returns a list of all connected players.
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_get_player_info
|
||||
rpc GetPlayers(GetPlayersRequest) returns (GetPlayersResponse) {}
|
||||
|
||||
// Kick a specified player from the server with a message
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_kick
|
||||
rpc KickPlayer(KickPlayerRequest) returns (KickPlayerResponse) {}
|
||||
|
||||
// Force a player into a slot / coalition.
|
||||
// To move the player back into spectators, use the following pseudo:
|
||||
// `ForcePlayerSlot({ player_id: ..., coalition: NEUTRAL, slot_id: "" })`
|
||||
rpc ForcePlayerSlot(ForcePlayerSlotRequest)
|
||||
returns (ForcePlayerSlotResponse) {}
|
||||
}
|
||||
|
||||
message SendChatToRequest {
|
||||
// the message to send in the chat
|
||||
string message = 1;
|
||||
// the target player of the direct message
|
||||
uint32 target_player_id = 2;
|
||||
}
|
||||
|
||||
message SendChatToResponse {
|
||||
}
|
||||
|
||||
message SendChatRequest {
|
||||
// the message to send in the chat
|
||||
string message = 1;
|
||||
// which coalition? DCS only supports ALL or NEUTRAL
|
||||
// (only applicable to send_chat)
|
||||
dcs.common.v0.Coalition coalition = 2;
|
||||
}
|
||||
|
||||
message SendChatResponse {
|
||||
}
|
||||
|
||||
message GetPlayersRequest {
|
||||
}
|
||||
|
||||
message GetPlayersResponse {
|
||||
message GetPlayerInfo {
|
||||
// the player id
|
||||
uint32 id = 1;
|
||||
// player's online name
|
||||
string name = 2;
|
||||
// coalition which player is slotted in
|
||||
dcs.common.v0.Coalition coalition = 3;
|
||||
// the slot identifier
|
||||
string slot = 4;
|
||||
// the ping of the player
|
||||
uint32 ping = 5;
|
||||
// the connection ip address and port the client
|
||||
// has established with the server
|
||||
string remote_address = 6;
|
||||
// the unique identifier for the player
|
||||
string ucid = 7;
|
||||
// abbreviated language (locale) e.g. "en"
|
||||
string locale = 8;
|
||||
}
|
||||
|
||||
// list of all the players connected to the server
|
||||
repeated GetPlayerInfo players = 1;
|
||||
}
|
||||
|
||||
message ForcePlayerSlotRequest {
|
||||
uint32 player_id = 1;
|
||||
dcs.common.v0.Coalition coalition = 2;
|
||||
string slot_id = 3;
|
||||
}
|
||||
|
||||
message ForcePlayerSlotResponse {
|
||||
}
|
||||
|
||||
message KickPlayerRequest {
|
||||
uint32 id = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
message KickPlayerResponse {
|
||||
}
|
||||
106
crates/dcs-grpc/protos/dcs/srs/v0/srs.proto
Normal file
106
crates/dcs-grpc/protos/dcs/srs/v0/srs.proto
Normal file
@@ -0,0 +1,106 @@
|
||||
syntax = "proto3";
|
||||
package dcs.srs.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Srs";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/srs";
|
||||
|
||||
service SrsService {
|
||||
// Synthesize text to speech and transmit it over SRS. By default, this blocks until a
|
||||
// transmission completed (unless `async` is set to `true`). This can be used to prevent
|
||||
// transmission to overlap each other, by not sending another transmission on the same frequency
|
||||
// until you've received the response from the previous transmission on that frequency. However,
|
||||
// it does not block or prevent any other client from transmitting over the same frequency at the
|
||||
// same time.
|
||||
rpc Transmit(TransmitRequest) returns (TransmitResponse) {}
|
||||
|
||||
// Retrieve a list of units (players) and their active frequencies that are connected to SRS.
|
||||
rpc GetClients(GetClientsRequest) returns (GetClientsResponse) {}
|
||||
}
|
||||
|
||||
message TransmitRequest {
|
||||
// The text that is synthesized to speech and transmitted to SRS. Supports SSML tags (you should
|
||||
// not wrap the text in the root `<speak>` tag though).
|
||||
string ssml = 1;
|
||||
|
||||
// The plain text without any transformations made to it for the purpose of getting it spoken out
|
||||
// as desired (no SSML tags, no FOUR NINER instead of 49, ...). Even though this field is
|
||||
// optional, please consider providing it as it can be used to display the spoken text to players
|
||||
// with hearing impairments.
|
||||
optional string plaintext = 2;
|
||||
|
||||
// The radio frequency in Hz the transmission is send to. Example: 251000000
|
||||
// for 251.00MHz.
|
||||
uint64 frequency = 3;
|
||||
|
||||
// Name of the SRS client. Defaults to "DCS-gRPC".
|
||||
optional string srs_client_name = 4;
|
||||
|
||||
// The origin of the transmission. Relevant if the SRS server has "Line of
|
||||
// Sight" and/or "Distance Limit" enabled.
|
||||
dcs.common.v0.InputPosition position = 5;
|
||||
|
||||
// The coalition of the transmission. Relevant if the SRS server has "Secure
|
||||
// Coalition Radios" enabled. Only Blue and Red are supported, all other
|
||||
// values will fallback to Spectator.
|
||||
dcs.common.v0.Coalition coalition = 6;
|
||||
|
||||
// Whether to keep the request open until the whole transmission was sent. If
|
||||
// enabled, you can send the next transmission after you've received the
|
||||
// response for the previous one and be sure that they don't overlap (talk
|
||||
// over each other). If disabled, you'll receive a response right away (kind
|
||||
// of fire and forget). You can use the returned duration as a spacing between
|
||||
// TTS requests to prevent the overlap of multiple playbacks yourself.
|
||||
bool async =7;
|
||||
|
||||
message Aws {
|
||||
// The voice the text is synthesized in, see:
|
||||
// https://docs.aws.amazon.com/polly/latest/dg/voicelist.html
|
||||
optional string voice = 1;
|
||||
}
|
||||
|
||||
message Azure {
|
||||
// The voice the text is synthesized in, see:
|
||||
// https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support
|
||||
optional string voice = 1;
|
||||
}
|
||||
|
||||
message GCloud {
|
||||
// The voice the text is synthesized in, see:
|
||||
// https://cloud.google.com/text-to-speech/docs/voices
|
||||
optional string voice = 1;
|
||||
}
|
||||
|
||||
message Windows {
|
||||
// The voice the text is synthesized in.
|
||||
optional string voice = 1;
|
||||
}
|
||||
|
||||
// Optional TTS provider to be use. Defaults to the one configured in your
|
||||
// config or to Windows' built-in TTS.
|
||||
oneof provider {
|
||||
Aws aws = 8;
|
||||
Azure azure = 9;
|
||||
GCloud gcloud = 10;
|
||||
Windows win = 11;
|
||||
}
|
||||
}
|
||||
|
||||
message TransmitResponse {
|
||||
// The duration in milliseconds it roughly takes to speak the transmission.
|
||||
uint32 duration_ms = 1;
|
||||
}
|
||||
|
||||
message GetClientsRequest {
|
||||
}
|
||||
|
||||
message GetClientsResponse {
|
||||
message Client {
|
||||
// The unit that is connected to SRS.
|
||||
dcs.common.v0.Unit unit = 1;
|
||||
|
||||
// The radio frequencies in Hz the unit is connected to.
|
||||
repeated uint64 frequencies = 2;
|
||||
}
|
||||
|
||||
repeated Client clients = 1;
|
||||
}
|
||||
46
crates/dcs-grpc/protos/dcs/timer/v0/timer.proto
Normal file
46
crates/dcs-grpc/protos/dcs/timer/v0/timer.proto
Normal file
@@ -0,0 +1,46 @@
|
||||
syntax = "proto3";
|
||||
package dcs.timer.v0;
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Timer";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/timer";
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_singleton_timer
|
||||
service TimerService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getTime
|
||||
rpc GetTime(GetTimeRequest) returns (GetTimeResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getAbsTime
|
||||
rpc GetAbsoluteTime(GetAbsoluteTimeRequest)
|
||||
returns (GetAbsoluteTimeResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getTime0
|
||||
rpc GetTimeZero(GetTimeZeroRequest) returns (GetTimeZeroResponse) {}
|
||||
}
|
||||
|
||||
message GetTimeRequest {
|
||||
}
|
||||
|
||||
message GetTimeResponse {
|
||||
double time = 1;
|
||||
}
|
||||
|
||||
message GetAbsoluteTimeRequest {
|
||||
}
|
||||
|
||||
message GetAbsoluteTimeResponse {
|
||||
// The current time in seconds since 00:00 of the start date of the mission.
|
||||
double time = 1;
|
||||
uint32 day = 2;
|
||||
uint32 month = 3;
|
||||
int32 year = 4;
|
||||
}
|
||||
|
||||
message GetTimeZeroRequest {
|
||||
}
|
||||
|
||||
message GetTimeZeroResponse {
|
||||
// The time in seconds since 00:00.
|
||||
double time = 1;
|
||||
uint32 day = 2;
|
||||
uint32 month = 3;
|
||||
int32 year = 4;
|
||||
}
|
||||
279
crates/dcs-grpc/protos/dcs/trigger/v0/trigger.proto
Normal file
279
crates/dcs-grpc/protos/dcs/trigger/v0/trigger.proto
Normal file
@@ -0,0 +1,279 @@
|
||||
syntax = "proto3";
|
||||
package dcs.trigger.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Trigger";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/trigger";
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_singleton_trigger
|
||||
service TriggerService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_outText
|
||||
rpc OutText(OutTextRequest) returns (OutTextResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_outTextForCoalition
|
||||
rpc OutTextForCoalition(OutTextForCoalitionRequest)
|
||||
returns (OutTextForCoalitionResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_outTextForGroup
|
||||
rpc OutTextForGroup(OutTextForGroupRequest)
|
||||
returns (OutTextForGroupResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_outTextForUnit
|
||||
rpc OutTextForUnit(OutTextForUnitRequest)
|
||||
returns (OutTextForUnitResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getUserFlag
|
||||
rpc GetUserFlag(GetUserFlagRequest) returns (GetUserFlagResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_setUserFlag
|
||||
rpc SetUserFlag(SetUserFlagRequest) returns (SetUserFlagResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_markToAll
|
||||
rpc MarkToAll(MarkToAllRequest) returns (MarkToAllResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_markToCoalition
|
||||
rpc MarkToCoalition(MarkToCoalitionRequest)
|
||||
returns (MarkToCoalitionResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_markToGroup
|
||||
rpc MarkToGroup(MarkToGroupRequest) returns (MarkToGroupResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_markupToAll
|
||||
rpc MarkupToAll(MarkupToAllRequest) returns (MarkupToAllResponse) {}
|
||||
|
||||
// Uses markupToAll under the hood but enforces a coalition to be specified
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_markupToAll
|
||||
rpc MarkupToCoalition(MarkupToCoalitionRequest)
|
||||
returns (MarkupToCoalitionResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_removeMark
|
||||
rpc RemoveMark(RemoveMarkRequest) returns (RemoveMarkResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_explosion
|
||||
rpc Explosion(ExplosionRequest) returns (ExplosionResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_smoke
|
||||
rpc Smoke(SmokeRequest) returns (SmokeResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_illuminationBomb
|
||||
rpc IlluminationBomb(IlluminationBombRequest)
|
||||
returns (IlluminationBombResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_signalFlare
|
||||
rpc SignalFlare(SignalFlareRequest) returns (SignalFlareResponse) {}
|
||||
}
|
||||
|
||||
message OutTextRequest {
|
||||
string text = 1;
|
||||
int32 display_time = 2;
|
||||
bool clear_view = 3;
|
||||
}
|
||||
|
||||
message OutTextResponse {
|
||||
}
|
||||
|
||||
message OutTextForCoalitionRequest {
|
||||
string text = 1;
|
||||
int32 display_time = 2;
|
||||
bool clear_view = 3;
|
||||
dcs.common.v0.Coalition coalition = 4;
|
||||
}
|
||||
|
||||
message OutTextForCoalitionResponse {
|
||||
}
|
||||
|
||||
message OutTextForGroupRequest {
|
||||
string text = 1;
|
||||
int32 display_time = 2;
|
||||
bool clear_view = 3;
|
||||
uint32 group_id = 4;
|
||||
}
|
||||
|
||||
message OutTextForGroupResponse {
|
||||
}
|
||||
|
||||
message OutTextForUnitRequest {
|
||||
string text = 1;
|
||||
int32 display_time = 2;
|
||||
bool clear_view = 3;
|
||||
uint32 unit_id = 4;
|
||||
}
|
||||
|
||||
message OutTextForUnitResponse {
|
||||
}
|
||||
|
||||
message GetUserFlagRequest {
|
||||
string flag = 1;
|
||||
}
|
||||
|
||||
message GetUserFlagResponse {
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
message SetUserFlagRequest {
|
||||
string flag = 1;
|
||||
uint32 value = 2;
|
||||
}
|
||||
|
||||
message SetUserFlagResponse {
|
||||
}
|
||||
|
||||
message MarkToAllRequest {
|
||||
string text = 2;
|
||||
dcs.common.v0.InputPosition position = 3;
|
||||
bool read_only = 4;
|
||||
string message = 5;
|
||||
}
|
||||
|
||||
message MarkToAllResponse {
|
||||
uint32 id = 1;
|
||||
}
|
||||
|
||||
message MarkToCoalitionRequest {
|
||||
uint32 id = 1;
|
||||
string text = 2;
|
||||
dcs.common.v0.InputPosition position = 3;
|
||||
dcs.common.v0.Coalition coalition = 4;
|
||||
bool read_only = 5;
|
||||
string message = 6;
|
||||
}
|
||||
|
||||
message MarkToCoalitionResponse {
|
||||
uint32 id = 1;
|
||||
}
|
||||
|
||||
message MarkToGroupRequest {
|
||||
uint32 id = 1;
|
||||
string text = 2;
|
||||
dcs.common.v0.InputPosition position = 3;
|
||||
uint32 group_id = 4;
|
||||
bool read_only = 5;
|
||||
string message = 6;
|
||||
}
|
||||
|
||||
message MarkToGroupResponse {
|
||||
uint32 id = 1;
|
||||
}
|
||||
|
||||
message RemoveMarkRequest {
|
||||
uint32 id = 1;
|
||||
}
|
||||
|
||||
message RemoveMarkResponse {
|
||||
}
|
||||
|
||||
message ExplosionRequest {
|
||||
dcs.common.v0.InputPosition position = 1;
|
||||
uint32 power = 2;
|
||||
}
|
||||
|
||||
message ExplosionResponse {
|
||||
}
|
||||
|
||||
message SmokeRequest {
|
||||
enum SmokeColor {
|
||||
SMOKE_COLOR_UNSPECIFIED = 0;
|
||||
SMOKE_COLOR_GREEN = 1;
|
||||
SMOKE_COLOR_RED = 2;
|
||||
SMOKE_COLOR_WHITE = 3;
|
||||
SMOKE_COLOR_ORANGE = 4;
|
||||
SMOKE_COLOR_BLUE = 5;
|
||||
}
|
||||
|
||||
// Altitude parameter will be ignored. Smoke always eminates from ground
|
||||
// level which will be calculated server-side
|
||||
dcs.common.v0.InputPosition position = 1;
|
||||
SmokeColor color = 2;
|
||||
}
|
||||
|
||||
message SmokeResponse {
|
||||
}
|
||||
|
||||
message IlluminationBombRequest {
|
||||
// The altitude of Illumination Bombs is meters above ground. Ground level
|
||||
// will be calculated server-side
|
||||
dcs.common.v0.InputPosition position = 1;
|
||||
uint32 power = 2;
|
||||
}
|
||||
|
||||
message IlluminationBombResponse {
|
||||
}
|
||||
|
||||
message SignalFlareRequest {
|
||||
enum FlareColor {
|
||||
FLARE_COLOR_UNSPECIFIED = 0;
|
||||
FLARE_COLOR_GREEN = 1;
|
||||
FLARE_COLOR_RED = 2;
|
||||
FLARE_COLOR_WHITE = 3;
|
||||
FLARE_COLOR_YELLOW = 4;
|
||||
}
|
||||
|
||||
// Altitude parameter will be ignored. Signal flares always fire from
|
||||
// ground level which will be calculated server-side
|
||||
dcs.common.v0.InputPosition position = 1;
|
||||
FlareColor color = 2;
|
||||
uint32 azimuth = 3;
|
||||
}
|
||||
|
||||
message SignalFlareResponse {
|
||||
}
|
||||
|
||||
enum LineType {
|
||||
// protolint:disable:next ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH
|
||||
LINE_TYPE_NO_LINE = 0;
|
||||
LINE_TYPE_SOLID = 1;
|
||||
LINE_TYPE_DASHED = 2;
|
||||
LINE_TYPE_DOTTED = 3;
|
||||
LINE_TYPE_DOT_DASH = 4;
|
||||
LINE_TYPE_LONG_DASH = 5;
|
||||
LINE_TYPE_TWO_DASH = 6;
|
||||
}
|
||||
|
||||
// Represents an RGBA color but instead of using 0-255 as the color
|
||||
// values it uses 0 to 1. A red color with 50% transparency would be
|
||||
// RGBA of 1, 0, 0, 0.5
|
||||
message Color {
|
||||
double red = 1;
|
||||
double green = 2;
|
||||
double blue = 3;
|
||||
double alpha = 4;
|
||||
}
|
||||
|
||||
enum Shape {
|
||||
SHAPE_UNSPECIFIED = 0;
|
||||
SHAPE_LINE = 1;
|
||||
SHAPE_CIRCLE = 2;
|
||||
SHAPE_RECT = 3;
|
||||
SHAPE_ARROW = 4;
|
||||
SHAPE_TEXT = 5;
|
||||
SHAPE_QUAD = 6;
|
||||
SHAPE_FREEFORM = 7;
|
||||
}
|
||||
|
||||
message MarkupToAllRequest {
|
||||
Shape shape = 1;
|
||||
repeated dcs.common.v0.InputPosition points = 2;
|
||||
Color border_color = 3;
|
||||
Color fill_color = 4;
|
||||
LineType line_type = 5;
|
||||
bool read_only = 6;
|
||||
string message = 7;
|
||||
}
|
||||
|
||||
message MarkupToAllResponse {
|
||||
uint32 id = 1;
|
||||
}
|
||||
|
||||
message MarkupToCoalitionRequest {
|
||||
Shape shape = 1;
|
||||
dcs.common.v0.Coalition coalition = 2;
|
||||
repeated dcs.common.v0.InputPosition points = 3;
|
||||
Color border_color = 4;
|
||||
Color fill_color = 5;
|
||||
LineType line_type = 6;
|
||||
bool read_only = 7;
|
||||
string message = 8;
|
||||
}
|
||||
|
||||
message MarkupToCoalitionResponse {
|
||||
uint32 id = 1;
|
||||
}
|
||||
106
crates/dcs-grpc/protos/dcs/unit/v0/unit.proto
Normal file
106
crates/dcs-grpc/protos/dcs/unit/v0/unit.proto
Normal file
@@ -0,0 +1,106 @@
|
||||
syntax = "proto3";
|
||||
package dcs.unit.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.Unit";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/unit";
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_Class_Unit
|
||||
service UnitService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getRadar
|
||||
rpc GetRadar(GetRadarRequest) returns (GetRadarResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getPoint
|
||||
rpc GetPosition(GetPositionRequest) returns (GetPositionResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getPlayerName
|
||||
rpc GetPlayerName(GetPlayerNameRequest) returns (GetPlayerNameResponse) {}
|
||||
|
||||
rpc GetDescriptor(GetDescriptorRequest) returns (GetDescriptorResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_enableEmission
|
||||
rpc SetEmission(SetEmissionRequest) returns (SetEmissionResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getByName
|
||||
rpc Get(GetRequest) returns (GetResponse) {}
|
||||
|
||||
/**
|
||||
* Get information about the unit in 3D space, including its position,
|
||||
* orientation and velocity.
|
||||
*/
|
||||
rpc GetTransform(GetTransformRequest) returns (GetTransformResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_destroy
|
||||
rpc Destroy(DestroyRequest) returns (DestroyResponse) {}
|
||||
}
|
||||
|
||||
message GetRadarRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetRadarResponse {
|
||||
bool active = 1;
|
||||
dcs.common.v0.Target target = 2;
|
||||
}
|
||||
|
||||
message GetPositionRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetPositionResponse {
|
||||
dcs.common.v0.Position position = 1;
|
||||
}
|
||||
|
||||
message GetTransformRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetTransformResponse {
|
||||
// Time in seconds since the scenario started.
|
||||
double time = 1;
|
||||
// The position of the unit
|
||||
dcs.common.v0.Position position = 2;
|
||||
// The orientation of the unit in both 2D and 3D space
|
||||
dcs.common.v0.Orientation orientation = 3;
|
||||
// The velocity of the unit in both 2D and 3D space
|
||||
dcs.common.v0.Velocity velocity = 4;
|
||||
}
|
||||
|
||||
message GetPlayerNameRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetPlayerNameResponse {
|
||||
optional string player_name = 1;
|
||||
}
|
||||
|
||||
message GetDescriptorRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// TODO fill these in as and when we need em
|
||||
message GetDescriptorResponse {
|
||||
repeated string attributes = 1;
|
||||
}
|
||||
|
||||
message SetEmissionRequest {
|
||||
string name = 1;
|
||||
bool emitting = 2;
|
||||
}
|
||||
|
||||
message SetEmissionResponse {
|
||||
}
|
||||
|
||||
message GetRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message GetResponse {
|
||||
dcs.common.v0.Unit unit = 1;
|
||||
}
|
||||
|
||||
message DestroyRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message DestroyResponse {
|
||||
}
|
||||
39
crates/dcs-grpc/protos/dcs/world/v0/world.proto
Normal file
39
crates/dcs-grpc/protos/dcs/world/v0/world.proto
Normal file
@@ -0,0 +1,39 @@
|
||||
syntax = "proto3";
|
||||
package dcs.world.v0;
|
||||
import "dcs/common/v0/common.proto";
|
||||
option csharp_namespace = "RurouniJones.Dcs.Grpc.V0.World";
|
||||
option go_package = "github.com/DCS-gRPC/go-bindings/dcs/v0/world";
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_singleton_world
|
||||
service WorldService {
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getAirbases
|
||||
rpc GetAirbases(GetAirbasesRequest) returns (GetAirbasesResponse) {}
|
||||
|
||||
// https://wiki.hoggitworld.com/view/DCS_func_getMarkPanels
|
||||
rpc GetMarkPanels(GetMarkPanelsRequest) returns (GetMarkPanelsResponse) {}
|
||||
|
||||
// Returns the theatre (Map name) of the mission
|
||||
rpc GetTheatre(GetTheatreRequest) returns (GetTheatreResponse) {}
|
||||
}
|
||||
|
||||
message GetAirbasesRequest {
|
||||
dcs.common.v0.Coalition coalition = 1;
|
||||
}
|
||||
|
||||
message GetAirbasesResponse {
|
||||
repeated dcs.common.v0.Airbase airbases = 1;
|
||||
}
|
||||
|
||||
message GetMarkPanelsRequest {
|
||||
}
|
||||
|
||||
message GetMarkPanelsResponse {
|
||||
repeated dcs.common.v0.MarkPanel mark_panels = 1;
|
||||
}
|
||||
|
||||
message GetTheatreRequest {
|
||||
}
|
||||
|
||||
message GetTheatreResponse {
|
||||
string theatre = 1;
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/atmosphere.rs
Normal file
3
crates/dcs-grpc/src/dcs/atmosphere.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.atmosphere.v0");
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/coalition.rs
Normal file
3
crates/dcs-grpc/src/dcs/coalition.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.coalition.v0");
|
||||
}
|
||||
151
crates/dcs-grpc/src/dcs/common.rs
Normal file
151
crates/dcs-grpc/src/dcs/common.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
pub mod v0 {
|
||||
use std::ops::Neg;
|
||||
|
||||
tonic::include_proto!("dcs.common.v0");
|
||||
|
||||
#[derive(Default, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct RawTransform {
|
||||
pub position: Option<Position>,
|
||||
pub position_north: Option<Vector>,
|
||||
pub forward: Option<Vector>,
|
||||
pub right: Option<Vector>,
|
||||
pub up: Option<Vector>,
|
||||
pub velocity: Option<Vector>,
|
||||
}
|
||||
|
||||
pub(crate) struct Transform {
|
||||
pub position: Position,
|
||||
pub orientation: Orientation,
|
||||
pub velocity: Velocity,
|
||||
}
|
||||
|
||||
impl From<RawTransform> for Transform {
|
||||
fn from(raw: RawTransform) -> Self {
|
||||
let RawTransform {
|
||||
position,
|
||||
position_north,
|
||||
forward,
|
||||
right,
|
||||
up,
|
||||
velocity,
|
||||
} = raw;
|
||||
let position = position.unwrap_or_default();
|
||||
let position_north = position_north.unwrap_or_default();
|
||||
let forward = forward.unwrap_or_default();
|
||||
let right = right.unwrap_or_default();
|
||||
let up = up.unwrap_or_default();
|
||||
let velocity = velocity.unwrap_or_default();
|
||||
|
||||
let projection_error =
|
||||
(position_north.z - position.u).atan2(position_north.x - position.v);
|
||||
let heading = forward.z.atan2(forward.x);
|
||||
|
||||
let orientation = Orientation {
|
||||
heading: {
|
||||
let heading = heading.to_degrees();
|
||||
if heading < 0.0 {
|
||||
heading + 360.0
|
||||
} else {
|
||||
heading
|
||||
}
|
||||
},
|
||||
yaw: (heading - projection_error).to_degrees(),
|
||||
roll: right.y.asin().neg().to_degrees(),
|
||||
pitch: forward.y.asin().to_degrees(),
|
||||
forward: Some(forward),
|
||||
right: Some(right),
|
||||
up: Some(up),
|
||||
};
|
||||
|
||||
let velocity = Velocity {
|
||||
heading: {
|
||||
let heading = velocity.z.atan2(velocity.x).to_degrees();
|
||||
if heading < 0.0 {
|
||||
heading + 360.0
|
||||
} else {
|
||||
heading
|
||||
}
|
||||
},
|
||||
speed: (velocity.x.powi(2) + velocity.z.powi(2)).sqrt(),
|
||||
velocity: Some(velocity),
|
||||
};
|
||||
|
||||
Transform {
|
||||
position,
|
||||
orientation,
|
||||
velocity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct UnitIntermediate {
|
||||
id: u32,
|
||||
name: String,
|
||||
callsign: String,
|
||||
coalition: i32,
|
||||
r#type: String,
|
||||
player_name: Option<String>,
|
||||
group: Option<Group>,
|
||||
number_in_group: u32,
|
||||
raw_transform: Option<RawTransform>,
|
||||
}
|
||||
|
||||
impl From<UnitIntermediate> for Unit {
|
||||
fn from(i: UnitIntermediate) -> Self {
|
||||
let UnitIntermediate {
|
||||
id,
|
||||
name,
|
||||
callsign,
|
||||
coalition,
|
||||
r#type,
|
||||
player_name,
|
||||
group,
|
||||
number_in_group,
|
||||
raw_transform,
|
||||
} = i;
|
||||
let transform = Transform::from(raw_transform.unwrap_or_default());
|
||||
Unit {
|
||||
id,
|
||||
name,
|
||||
callsign,
|
||||
coalition,
|
||||
r#type,
|
||||
position: Some(transform.position),
|
||||
orientation: Some(transform.orientation),
|
||||
velocity: Some(transform.velocity),
|
||||
player_name,
|
||||
group,
|
||||
number_in_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WeaponIntermediate {
|
||||
id: u32,
|
||||
r#type: String,
|
||||
raw_transform: Option<RawTransform>,
|
||||
}
|
||||
|
||||
impl From<WeaponIntermediate> for Weapon {
|
||||
fn from(i: WeaponIntermediate) -> Self {
|
||||
let WeaponIntermediate {
|
||||
id,
|
||||
r#type,
|
||||
raw_transform,
|
||||
} = i;
|
||||
let transform = Transform::from(raw_transform.unwrap_or_default());
|
||||
Weapon {
|
||||
id,
|
||||
r#type,
|
||||
position: Some(transform.position),
|
||||
orientation: Some(transform.orientation),
|
||||
velocity: Some(transform.velocity),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/controller.rs
Normal file
3
crates/dcs-grpc/src/dcs/controller.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.controller.v0");
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/custom.rs
Normal file
3
crates/dcs-grpc/src/dcs/custom.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.custom.v0");
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/group.rs
Normal file
3
crates/dcs-grpc/src/dcs/group.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.group.v0");
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/hook.rs
Normal file
3
crates/dcs-grpc/src/dcs/hook.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.hook.v0");
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/mission.rs
Normal file
3
crates/dcs-grpc/src/dcs/mission.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.mission.v0");
|
||||
}
|
||||
278
crates/dcs-grpc/src/dcs/mod.rs
Normal file
278
crates/dcs-grpc/src/dcs/mod.rs
Normal file
@@ -0,0 +1,278 @@
|
||||
// Current recommendation as of
|
||||
// https://github.com/tokio-rs/prost/issues/661#issuecomment-1156606409
|
||||
#![allow(clippy::derive_partial_eq_without_eq)]
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
// use crate::{call::Element, dcs::net::v0::SendChatRequest};
|
||||
// use std::{cmp::Ordering, collections::HashMap, sync::Arc, time::Duration};
|
||||
// use tokio::{sync::RwLock, time::sleep};
|
||||
|
||||
// use self::{
|
||||
// common::v0::Coalition,
|
||||
// mission::v0::{
|
||||
// mission_service_client::MissionServiceClient, stream_units_response::Update,
|
||||
// StreamUnitsRequest,
|
||||
// },
|
||||
// net::v0::net_service_client::NetServiceClient,
|
||||
// };
|
||||
|
||||
pub mod atmosphere;
|
||||
pub mod coalition;
|
||||
pub mod common;
|
||||
pub mod controller;
|
||||
pub mod custom;
|
||||
pub mod group;
|
||||
pub mod hook;
|
||||
pub mod mission;
|
||||
pub mod net;
|
||||
pub mod srs;
|
||||
pub mod timer;
|
||||
pub mod trigger;
|
||||
pub mod unit;
|
||||
mod utils;
|
||||
pub mod world;
|
||||
|
||||
// // In the general part of the world DCS is set in, the compass drift is about 5 degrees
|
||||
// // This should probably be configurable as it depends on the map.
|
||||
// // (I just remember reading somewhere that the heading drift is about 5°, cant find proof now but in testing it lines up)
|
||||
// // https://en.wikipedia.org/wiki/Magnetic_declination
|
||||
// // https://github.com/DCS-gRPC/rust-server/issues/197
|
||||
// const COMPASS_DRIFT: i32 = -5;
|
||||
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub struct Position {
|
||||
// pub lat: f64,
|
||||
// pub long: f64,
|
||||
// pub altitude: f64,
|
||||
// }
|
||||
|
||||
// impl Position {
|
||||
// pub fn distance_to_km(&self, other: &Position) -> f64 {
|
||||
// let radius_of_earth_in_km: f64 = 6371.0;
|
||||
// let delta_lat = (other.lat - self.lat).to_radians();
|
||||
// let delta_long = (other.long - self.long).to_radians();
|
||||
|
||||
// let a = (delta_lat / 2.0).sin() * (delta_lat / 2.0).sin()
|
||||
// + self.lat.to_radians().cos()
|
||||
// * other.lat.to_radians().cos()
|
||||
// * (delta_long / 2.0).sin()
|
||||
// * (delta_long / 2.0).sin();
|
||||
|
||||
// let c = 2.0 * (a.sqrt().atan2((1.0 - a).sqrt()));
|
||||
// radius_of_earth_in_km * c
|
||||
// }
|
||||
|
||||
// pub fn distance_to_nmi(&self, other: &Position) -> f64 {
|
||||
// self.distance_to_km(other) * 0.5399568035
|
||||
// }
|
||||
|
||||
// pub fn get_bearing_to(&self, other: &Position) -> i32 {
|
||||
// let delta_long = (other.long - self.long).to_radians();
|
||||
|
||||
// let y =
|
||||
// (other.long.to_radians() - self.long.to_radians()).sin() * other.lat.to_radians().cos();
|
||||
// let x = self.lat.to_radians().cos() * other.lat.to_radians().sin()
|
||||
// - self.lat.to_radians().sin() * other.lat.to_radians().cos() * delta_long.cos();
|
||||
|
||||
// (((y.atan2(x).to_degrees() + 360.0) % 360.0) as i32) + COMPASS_DRIFT
|
||||
// }
|
||||
|
||||
// pub fn feet(&self) -> f64 {
|
||||
// self.altitude * 3.28084
|
||||
// }
|
||||
|
||||
// pub fn angels(&self) -> i32 {
|
||||
// (self.feet() / 1000.0).round() as i32
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub struct Unit {
|
||||
// pub id: u32,
|
||||
// pub name: String,
|
||||
// pub player: Option<String>,
|
||||
// pub callsign: String,
|
||||
// pub group_name: Option<String>,
|
||||
// pub coalition: i32,
|
||||
// pub r#type: String,
|
||||
// pub position: Position,
|
||||
// pub heading: f64,
|
||||
// pub speed: f64,
|
||||
// }
|
||||
|
||||
// #[derive(Clone)]
|
||||
// pub struct RpcClient {
|
||||
// base_url: String,
|
||||
// units: Arc<RwLock<HashMap<u32, Unit>>>,
|
||||
// }
|
||||
|
||||
// impl RpcClient {
|
||||
// pub fn new(base_url: &str) -> Self {
|
||||
// let units = Arc::new(RwLock::new(HashMap::new()));
|
||||
|
||||
// let db = units.clone();
|
||||
// let url = base_url.to_owned();
|
||||
// tokio::spawn(async move {
|
||||
// loop {
|
||||
// let Ok(mut client) = MissionServiceClient::connect(url.clone()).await else {
|
||||
// eprintln!("Unable to connect to DCS. Make sure Dcs-gRPC is installed and available, retry in 5 seconds..");
|
||||
// sleep(Duration::from_secs(5)).await; // retry in 5 seconds
|
||||
// continue;
|
||||
// };
|
||||
|
||||
// let Ok(mut stream) = client
|
||||
// .stream_units(StreamUnitsRequest {
|
||||
// poll_rate: Some(10),
|
||||
// max_backoff: Some(30),
|
||||
// category: common::v0::GroupCategory::Airplane as i32,
|
||||
// })
|
||||
// .await
|
||||
// else {
|
||||
// continue;
|
||||
// };
|
||||
|
||||
// let mut message = stream.get_mut().message().await;
|
||||
// while let Ok(Some(next)) = &message {
|
||||
// if let Some(update) = &next.update {
|
||||
// match update {
|
||||
// Update::Gone(unit) => {
|
||||
// db.write().await.remove(&unit.id);
|
||||
// }
|
||||
// Update::Unit(unit) => {
|
||||
// let group_name = unit.group.as_ref().map(|g| g.name.clone());
|
||||
|
||||
// let lat = unit.position.as_ref().map_or(0.0, |p| p.lat);
|
||||
// let long = unit.position.as_ref().map_or(0.0, |p| p.lon);
|
||||
// let altitude = unit.position.as_ref().map_or(0.0, |p| p.alt);
|
||||
|
||||
// let heading = unit.orientation.as_ref().map_or(0.0, |o| o.heading);
|
||||
// let speed = unit.velocity.as_ref().map_or(0.0, |v| v.speed);
|
||||
|
||||
// // println!("updated: {}", &unit.callsign);
|
||||
|
||||
// db.write().await.insert(
|
||||
// unit.id,
|
||||
// Unit {
|
||||
// id: unit.id,
|
||||
// name: unit.name.clone(),
|
||||
// player: unit.player_name.clone(),
|
||||
// callsign: unit.callsign.clone(),
|
||||
// group_name,
|
||||
// coalition: unit.coalition,
|
||||
// r#type: unit.r#type.clone(),
|
||||
// position: Position {
|
||||
// lat,
|
||||
// long,
|
||||
// altitude,
|
||||
// },
|
||||
// heading,
|
||||
// speed,
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// message = stream.get_mut().message().await;
|
||||
// }
|
||||
// // if we get here, something went wrong, reconnect
|
||||
// eprintln!("Connection lost");
|
||||
// }
|
||||
// });
|
||||
|
||||
// Self {
|
||||
// base_url: base_url.to_string(),
|
||||
// units,
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Change to TransmitMessage when it becomes available in gRPC
|
||||
// // https://github.com/DCS-gRPC/rust-server/blob/main/STATUS.md?plain=1#L519
|
||||
// pub async fn send_text_message(&self, message: &str) {
|
||||
// let Ok(mut client) = NetServiceClient::connect(self.base_url.clone()).await else {
|
||||
// return;
|
||||
// };
|
||||
|
||||
// let request = SendChatRequest {
|
||||
// message: message.to_string(),
|
||||
// coalition: Coalition::All as i32,
|
||||
// // target_player_id: player_id,
|
||||
// };
|
||||
|
||||
// client.send_chat(request).await.ok();
|
||||
// }
|
||||
|
||||
// pub async fn get_unit_by_element(&self, element: &Element) -> Option<Unit> {
|
||||
// // Return a unit, do the math in the command code
|
||||
// let units = self.units.read().await;
|
||||
|
||||
// units
|
||||
// .iter()
|
||||
// .filter(|u| u.1.coalition == Coalition::Blue as i32)
|
||||
// .map(|u| u.1)
|
||||
// .find(|e| {
|
||||
// e.callsign.to_lowercase()
|
||||
// == format!(
|
||||
// "{}{}{}",
|
||||
// element.squadron.to_string(),
|
||||
// element.group,
|
||||
// element.unit
|
||||
// )
|
||||
// .to_lowercase()
|
||||
// })
|
||||
// .cloned()
|
||||
// }
|
||||
|
||||
// pub async fn get_unit_by_pilot(&self, pilot: &str) -> Option<Unit> {
|
||||
// // Return a unit, do the math in the command code
|
||||
// let units = self.units.read().await;
|
||||
// let pilot = pilot.to_string();
|
||||
|
||||
// units
|
||||
// .iter()
|
||||
// .filter(|u| u.1.coalition == Coalition::Blue as i32)
|
||||
// .map(|u| u.1)
|
||||
// .find(|e| {
|
||||
// let Some(player) = &e.player else {
|
||||
// return false;
|
||||
// };
|
||||
|
||||
// player == &pilot
|
||||
// })
|
||||
// .cloned()
|
||||
// }
|
||||
|
||||
// pub async fn get_nearest_hostile(&self, unit: &Unit) -> Option<Unit> {
|
||||
// let units = self.units.read().await;
|
||||
|
||||
// units
|
||||
// .iter()
|
||||
// .filter(|u| u.1.coalition == Coalition::Red as i32) // only reds
|
||||
// .map(|u| u.1)
|
||||
// .min_by(|a, b| {
|
||||
// let a_pos = &a.position; // some hostile A position
|
||||
// let b_pos = &b.position; // some hostile B position
|
||||
|
||||
// let a_dist = a_pos.distance_to_km(&unit.position);
|
||||
// let b_dist = b_pos.distance_to_km(&unit.position);
|
||||
|
||||
// a_dist.partial_cmp(&b_dist).unwrap()
|
||||
// })
|
||||
// .cloned()
|
||||
// }
|
||||
|
||||
// pub async fn get_hostiles_in_range_from(&self, unit: &Unit, distance: f64) -> Vec<Unit> {
|
||||
// let units = self.units.read().await;
|
||||
|
||||
// units
|
||||
// .iter()
|
||||
// .filter(|u| u.1.coalition == Coalition::Red as i32) // only reds
|
||||
// .map(|u| u.1)
|
||||
// .filter(|a| {
|
||||
// let a_pos = &a.position; // some hostile A position
|
||||
// let a_dist = a_pos.distance_to_nmi(&unit.position);
|
||||
// a_dist.partial_cmp(&distance).unwrap() == Ordering::Less
|
||||
// })
|
||||
// .cloned()
|
||||
// .collect::<Vec<_>>()
|
||||
// }
|
||||
// }
|
||||
3
crates/dcs-grpc/src/dcs/net.rs
Normal file
3
crates/dcs-grpc/src/dcs/net.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.net.v0");
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/srs.rs
Normal file
3
crates/dcs-grpc/src/dcs/srs.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.srs.v0");
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/timer.rs
Normal file
3
crates/dcs-grpc/src/dcs/timer.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.timer.v0");
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/trigger.rs
Normal file
3
crates/dcs-grpc/src/dcs/trigger.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.trigger.v0");
|
||||
}
|
||||
28
crates/dcs-grpc/src/dcs/unit.rs
Normal file
28
crates/dcs-grpc/src/dcs/unit.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
pub mod v0 {
|
||||
use crate::dcs::common::v0::{RawTransform, Transform};
|
||||
|
||||
tonic::include_proto!("dcs.unit.v0");
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GetTransformResponseIntermediate {
|
||||
time: f64,
|
||||
raw_transform: Option<RawTransform>,
|
||||
}
|
||||
|
||||
impl From<GetTransformResponseIntermediate> for GetTransformResponse {
|
||||
fn from(i: GetTransformResponseIntermediate) -> Self {
|
||||
let GetTransformResponseIntermediate {
|
||||
time,
|
||||
raw_transform,
|
||||
} = i;
|
||||
let transform = Transform::from(raw_transform.unwrap_or_default());
|
||||
GetTransformResponse {
|
||||
time,
|
||||
position: Some(transform.position),
|
||||
orientation: Some(transform.orientation),
|
||||
velocity: Some(transform.velocity),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
411
crates/dcs-grpc/src/dcs/utils.rs
Normal file
411
crates/dcs-grpc/src/dcs/utils.rs
Normal file
@@ -0,0 +1,411 @@
|
||||
/// Methods that can be used to serialize and deserialize [prost_types::Struct].
|
||||
pub mod proto_struct {
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
||||
use prost_types::value::Kind;
|
||||
use prost_types::{ListValue, Struct, Value};
|
||||
use serde::de::{MapAccess, Visitor};
|
||||
use serde::ser::{SerializeMap, SerializeSeq};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
pub fn serialize<S>(data: &Option<Struct>, se: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if let Some(data) = data {
|
||||
StructSe(data).serialize(se)
|
||||
} else {
|
||||
se.serialize_unit()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(de: D) -> Result<Option<Struct>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Option::<StructDe>::deserialize(de)?.map(|s| s.0))
|
||||
}
|
||||
|
||||
/// Serializable Wrapper around [prost_types::Struct].
|
||||
struct StructSe<'a>(&'a Struct);
|
||||
|
||||
impl<'a> Serialize for StructSe<'a> {
|
||||
fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut map = se.serialize_map(Some(self.0.fields.len()))?;
|
||||
for (key, val) in &self.0.fields {
|
||||
map.serialize_key(&key)?;
|
||||
map.serialize_value(&ValueSe(val))?;
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializable Wrapper around [prost_types::Struct].
|
||||
struct StructDe(Struct);
|
||||
|
||||
impl<'de> Deserialize<'de> for StructDe {
|
||||
fn deserialize<D>(de: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
de.deserialize_map(StructVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializable Wrapper around [prost_types::Value].
|
||||
struct ValueSe<'a>(&'a Value);
|
||||
|
||||
impl<'a> Serialize for ValueSe<'a> {
|
||||
fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if let Some(kind) = &self.0.kind {
|
||||
match kind {
|
||||
Kind::NullValue(_) => se.serialize_unit(),
|
||||
Kind::NumberValue(v) => v.serialize(se),
|
||||
Kind::StringValue(v) => v.serialize(se),
|
||||
Kind::BoolValue(v) => v.serialize(se),
|
||||
Kind::StructValue(v) => StructSe(v).serialize(se),
|
||||
Kind::ListValue(v) => {
|
||||
let mut seq = se.serialize_seq(Some(v.values.len()))?;
|
||||
for val in &v.values {
|
||||
seq.serialize_element(&ValueSe(val))?
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
se.serialize_none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializable Wrapper around [prost_types::Value].
|
||||
struct ValueDe(Value);
|
||||
|
||||
impl<'de> Deserialize<'de> for ValueDe {
|
||||
fn deserialize<D>(de: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
de.deserialize_any(ValueVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct StructVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for StructVisitor {
|
||||
type Value = StructDe;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("google.protobuf.Struct")
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
let mut fields = BTreeMap::new();
|
||||
while let Some((key, value)) = access.next_entry::<String, ValueDe>()? {
|
||||
fields.insert(key, value.0);
|
||||
}
|
||||
Ok(StructDe(Struct { fields }))
|
||||
}
|
||||
}
|
||||
|
||||
struct ValueVisitor;
|
||||
|
||||
impl ValueVisitor {
|
||||
fn visit_number<E>(self, v: impl TryInto<f64>) -> Result<ValueDe, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
v.try_into()
|
||||
.map(|v| {
|
||||
ValueDe(Value {
|
||||
kind: Some(Kind::NumberValue(v)),
|
||||
})
|
||||
})
|
||||
.map_err(|_| {
|
||||
serde::de::Error::invalid_type(serde::de::Unexpected::Other("f64"), &self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Visitor<'de> for ValueVisitor {
|
||||
type Value = ValueDe;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("google.protobuf.Value")
|
||||
}
|
||||
|
||||
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(ValueDe(Value {
|
||||
kind: Some(Kind::BoolValue(v)),
|
||||
}))
|
||||
}
|
||||
|
||||
fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_number(v)
|
||||
}
|
||||
|
||||
fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_number(v)
|
||||
}
|
||||
|
||||
fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_number(v)
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
i32::try_from(v)
|
||||
.map_err(|_| {
|
||||
serde::de::Error::invalid_type(serde::de::Unexpected::Other("i64"), &self)
|
||||
})
|
||||
.and_then(|v| self.visit_number(v))
|
||||
}
|
||||
|
||||
fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
i32::try_from(v)
|
||||
.map_err(|_| {
|
||||
serde::de::Error::invalid_type(serde::de::Unexpected::Other("i64"), &self)
|
||||
})
|
||||
.and_then(|v| self.visit_number(v))
|
||||
}
|
||||
|
||||
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_number(v)
|
||||
}
|
||||
|
||||
fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_number(v)
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_number(v)
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
u32::try_from(v)
|
||||
.map_err(|_| {
|
||||
serde::de::Error::invalid_type(serde::de::Unexpected::Other("i64"), &self)
|
||||
})
|
||||
.and_then(|v| self.visit_number(v))
|
||||
}
|
||||
|
||||
fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
u32::try_from(v)
|
||||
.map_err(|_| {
|
||||
serde::de::Error::invalid_type(serde::de::Unexpected::Other("i64"), &self)
|
||||
})
|
||||
.and_then(|v| self.visit_number(v))
|
||||
}
|
||||
|
||||
fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_number(v)
|
||||
}
|
||||
|
||||
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_number(v)
|
||||
}
|
||||
|
||||
fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(ValueDe(Value {
|
||||
kind: Some(Kind::StringValue(v.to_string())),
|
||||
}))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(ValueDe(Value {
|
||||
kind: Some(Kind::StringValue(v.to_string())),
|
||||
}))
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(ValueDe(Value {
|
||||
kind: Some(Kind::NullValue(0)),
|
||||
}))
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut values = Vec::with_capacity(seq.size_hint().unwrap_or(0));
|
||||
while let Some(val) = seq.next_element::<ValueDe>()? {
|
||||
values.push(val.0);
|
||||
}
|
||||
|
||||
Ok(ValueDe(Value {
|
||||
kind: Some(Kind::ListValue(ListValue { values })),
|
||||
}))
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
Ok(ValueDe(Value {
|
||||
kind: Some(Kind::StructValue(StructVisitor.visit_map(map)?.0)),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use prost_types::value::Kind;
|
||||
use prost_types::{ListValue, Struct, Value};
|
||||
|
||||
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
struct Message {
|
||||
#[serde(with = "crate::dcs::utils::proto_struct")]
|
||||
details: Option<Struct>,
|
||||
}
|
||||
|
||||
fn create_message(key: impl Into<String>, kind: Kind) -> Message {
|
||||
Message {
|
||||
details: Some(Struct {
|
||||
fields: [(key.into(), Value { kind: Some(kind) })]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null() {
|
||||
let m = create_message("null", Kind::NullValue(0));
|
||||
let json = serde_json::to_string(&m).unwrap();
|
||||
assert_eq!(json, r#"{"details":{"null":null}}"#);
|
||||
assert_eq!(serde_json::from_str::<Message>(&json).unwrap(), m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number() {
|
||||
let m = create_message("number", Kind::NumberValue(42.2223));
|
||||
let json = serde_json::to_string(&m).unwrap();
|
||||
assert_eq!(json, r#"{"details":{"number":42.2223}}"#);
|
||||
assert_eq!(serde_json::from_str::<Message>(&json).unwrap(), m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
let m = create_message("string", Kind::StringValue("dcs-grpc".to_string()));
|
||||
let json = serde_json::to_string(&m).unwrap();
|
||||
assert_eq!(json, r#"{"details":{"string":"dcs-grpc"}}"#);
|
||||
assert_eq!(serde_json::from_str::<Message>(&json).unwrap(), m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool() {
|
||||
let m = create_message("bool", Kind::BoolValue(true));
|
||||
let json = serde_json::to_string(&m).unwrap();
|
||||
assert_eq!(json, r#"{"details":{"bool":true}}"#);
|
||||
assert_eq!(serde_json::from_str::<Message>(&json).unwrap(), m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct() {
|
||||
let m = create_message(
|
||||
"nested",
|
||||
Kind::StructValue(Struct {
|
||||
fields: [
|
||||
(
|
||||
"number".to_string(),
|
||||
Value {
|
||||
kind: Some(Kind::NumberValue(42.0)),
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".to_string(),
|
||||
Value {
|
||||
kind: Some(Kind::StringValue("dcs-grpc".to_string())),
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
}),
|
||||
);
|
||||
let json = serde_json::to_string(&m).unwrap();
|
||||
assert_eq!(
|
||||
json,
|
||||
r#"{"details":{"nested":{"number":42.0,"string":"dcs-grpc"}}}"#
|
||||
);
|
||||
assert_eq!(serde_json::from_str::<Message>(&json).unwrap(), m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list() {
|
||||
let m = create_message(
|
||||
"list",
|
||||
Kind::ListValue(ListValue {
|
||||
values: vec![
|
||||
Value {
|
||||
kind: Some(Kind::NumberValue(42.0)),
|
||||
},
|
||||
Value {
|
||||
kind: Some(Kind::StringValue("dcs-grpc".to_string())),
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
let json = serde_json::to_string(&m).unwrap();
|
||||
assert_eq!(json, r#"{"details":{"list":[42.0,"dcs-grpc"]}}"#);
|
||||
assert_eq!(serde_json::from_str::<Message>(&json).unwrap(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
crates/dcs-grpc/src/dcs/world.rs
Normal file
3
crates/dcs-grpc/src/dcs/world.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod v0 {
|
||||
tonic::include_proto!("dcs.world.v0");
|
||||
}
|
||||
1
crates/dcs-grpc/src/lib.rs
Normal file
1
crates/dcs-grpc/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod dcs;
|
||||
16
crates/guardian_core/Cargo.toml
Normal file
16
crates/guardian_core/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "guardian_core"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bevy.workspace = true
|
||||
dcs-grpc.workspace = true
|
||||
tokio.workspace = true
|
||||
async-compat = "0.2.1"
|
||||
crossbeam-channel = "0.5.9"
|
||||
tonic = "0.10"
|
||||
28
crates/guardian_core/src/dcs.rs
Normal file
28
crates/guardian_core/src/dcs.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
mod mission;
|
||||
mod text;
|
||||
mod voice;
|
||||
|
||||
use crate::GrpcBaseUrl;
|
||||
|
||||
pub use self::mission::*;
|
||||
pub use self::text::*;
|
||||
|
||||
use bevy::app::{App, Plugin, ScheduleRunnerPlugin};
|
||||
pub use dcs_grpc::dcs::common::v0::{Coalition, Unit};
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct DcsPlugin;
|
||||
|
||||
impl Plugin for DcsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.world.get_resource_or_insert_with(GrpcBaseUrl::default);
|
||||
|
||||
// Make the app loop forever at 60fps.
|
||||
app.add_plugins(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(
|
||||
1.0 / 60.0,
|
||||
)));
|
||||
|
||||
app.add_plugins(MissionPlugin);
|
||||
app.add_plugins(TextPlugin);
|
||||
}
|
||||
}
|
||||
251
crates/guardian_core/src/dcs/mission.rs
Normal file
251
crates/guardian_core/src/dcs/mission.rs
Normal file
@@ -0,0 +1,251 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{GrpcBaseUrl, TokioResource};
|
||||
use bevy::{
|
||||
app::{App, Plugin, PreStartup, PreUpdate},
|
||||
core::Name,
|
||||
ecs::{
|
||||
component::Component,
|
||||
entity::Entity,
|
||||
event::{Event, EventReader, EventWriter},
|
||||
system::{Commands, Query, Res},
|
||||
},
|
||||
};
|
||||
use crossbeam_channel::{unbounded, Receiver};
|
||||
use dcs_grpc::dcs::{
|
||||
common::v0::{Coalition, GroupCategory::Airplane, Unit},
|
||||
mission::v0::{
|
||||
mission_service_client::MissionServiceClient, stream_units_response::Update,
|
||||
StreamUnitsRequest,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct MissionPlugin;
|
||||
|
||||
impl Plugin for MissionPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<UnitUpdatedEvent>();
|
||||
app.add_event::<UnitGoneEvent>();
|
||||
|
||||
app.add_systems(PreStartup, connect_to_grpc);
|
||||
app.add_systems(
|
||||
PreUpdate,
|
||||
(consume_stream_message, update_units, despawn_units),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_to_grpc(mut commands: Commands, tokio: Res<TokioResource>, url: Res<GrpcBaseUrl>) {
|
||||
let handle = &tokio.0;
|
||||
|
||||
let (tx, task) = unbounded();
|
||||
|
||||
let url = url.to_string();
|
||||
handle.spawn(async move {
|
||||
loop {
|
||||
let Ok(mut client) = MissionServiceClient::connect(url.clone()).await else {
|
||||
eprintln!("Connection failed: {}", url);
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(mut stream) = client
|
||||
.stream_units(StreamUnitsRequest {
|
||||
poll_rate: Some(10),
|
||||
max_backoff: Some(30),
|
||||
category: Airplane as i32,
|
||||
})
|
||||
.await
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut message = stream.get_mut().message().await;
|
||||
while let Ok(Some(next)) = &message {
|
||||
if let Some(update) = &next.update {
|
||||
tx.send(update.clone()).ok();
|
||||
}
|
||||
|
||||
message = stream.get_mut().message().await;
|
||||
}
|
||||
|
||||
eprintln!("Disconnected");
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
});
|
||||
|
||||
commands.spawn(UnitsRequestMessage(task));
|
||||
}
|
||||
|
||||
pub(crate) fn consume_stream_message(
|
||||
requests: Query<&UnitsRequestMessage>,
|
||||
mut ev_unit_updated: EventWriter<UnitUpdatedEvent>,
|
||||
mut ev_unit_gone: EventWriter<UnitGoneEvent>,
|
||||
) {
|
||||
for stream in requests.iter() {
|
||||
if let Ok(update) = stream.0.try_recv() {
|
||||
match update {
|
||||
Update::Gone(unit) => {
|
||||
ev_unit_gone.send(UnitGoneEvent(unit.id));
|
||||
}
|
||||
Update::Unit(unit) => {
|
||||
ev_unit_updated.send(UnitUpdatedEvent(unit));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_units(
|
||||
mut commands: Commands,
|
||||
units: Query<(Entity, &Id)>,
|
||||
mut ev: EventReader<UnitUpdatedEvent>,
|
||||
) {
|
||||
for event in ev.read() {
|
||||
let event = &event.0;
|
||||
|
||||
let mut e: Option<_> = None;
|
||||
for (ent, id) in units.iter() {
|
||||
if id.0 == event.id {
|
||||
e = commands.get_entity(ent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if e.is_none() {
|
||||
e = Some(commands.spawn((Id(event.id), Callsign(event.callsign.clone()))));
|
||||
}
|
||||
|
||||
let Some(mut e) = e else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
if let Some(position) = &event.position {
|
||||
e.insert(Position {
|
||||
lat: position.lat,
|
||||
long: position.lon,
|
||||
altitude: position.alt,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(orientation) = &event.orientation {
|
||||
e.insert(Heading(orientation.heading));
|
||||
}
|
||||
|
||||
if let Some(playername) = &event.player_name {
|
||||
e.insert(Player(playername.clone()));
|
||||
}
|
||||
|
||||
if let Some(group) = &event.group {
|
||||
e.insert(Group {
|
||||
id: group.id,
|
||||
unit: event.number_in_group,
|
||||
});
|
||||
}
|
||||
|
||||
e.insert(Name::new(event.name.clone()));
|
||||
|
||||
e.insert(UnitType(event.r#type.clone()));
|
||||
|
||||
e.insert(Side(
|
||||
Coalition::try_from(event.coalition).expect("Coalition to be correct"),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn despawn_units(
|
||||
mut commands: Commands,
|
||||
units: Query<(Entity, &Id)>,
|
||||
mut ev: EventReader<UnitGoneEvent>,
|
||||
) {
|
||||
for event in ev.read() {
|
||||
let gid = &event.0;
|
||||
for (ent, id) in units.iter() {
|
||||
if gid == &id.0 {
|
||||
commands.entity(ent).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
pub(crate) struct UnitUpdatedEvent(Unit);
|
||||
|
||||
#[derive(Event)]
|
||||
pub(crate) struct UnitGoneEvent(u32);
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct UnitsRequestMessage(Receiver<Update>);
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Id(pub u32);
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Callsign(pub String);
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct UnitType(pub String);
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Player(pub String);
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Side(pub Coalition);
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Position {
|
||||
pub lat: f64,
|
||||
pub long: f64,
|
||||
pub altitude: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Heading(pub f64);
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Group {
|
||||
pub id: u32,
|
||||
pub unit: u32,
|
||||
}
|
||||
|
||||
const COMPASS_DRIFT: i32 = -5;
|
||||
|
||||
impl Position {
|
||||
pub fn distance_to_km(&self, other: &Position) -> f64 {
|
||||
let radius_of_earth_in_km: f64 = 6371.0;
|
||||
let delta_lat = (other.lat - self.lat).to_radians();
|
||||
let delta_long = (other.long - self.long).to_radians();
|
||||
|
||||
let a = (delta_lat / 2.0).sin() * (delta_lat / 2.0).sin()
|
||||
+ self.lat.to_radians().cos()
|
||||
* other.lat.to_radians().cos()
|
||||
* (delta_long / 2.0).sin()
|
||||
* (delta_long / 2.0).sin();
|
||||
|
||||
let c = 2.0 * (a.sqrt().atan2((1.0 - a).sqrt()));
|
||||
radius_of_earth_in_km * c
|
||||
}
|
||||
|
||||
pub fn distance_to_nmi(&self, other: &Position) -> f64 {
|
||||
self.distance_to_km(other) * 0.5399568035
|
||||
}
|
||||
|
||||
pub fn get_bearing_to(&self, other: &Position) -> i32 {
|
||||
let delta_long = (other.long - self.long).to_radians();
|
||||
|
||||
let y =
|
||||
(other.long.to_radians() - self.long.to_radians()).sin() * other.lat.to_radians().cos();
|
||||
let x = self.lat.to_radians().cos() * other.lat.to_radians().sin()
|
||||
- self.lat.to_radians().sin() * other.lat.to_radians().cos() * delta_long.cos();
|
||||
|
||||
(((y.atan2(x).to_degrees() + 360.0) % 360.0) as i32) + COMPASS_DRIFT
|
||||
}
|
||||
|
||||
pub fn feet(&self) -> f64 {
|
||||
self.altitude * 3.28084
|
||||
}
|
||||
|
||||
pub fn angels(&self) -> i32 {
|
||||
(self.feet() / 1000.0).round() as i32
|
||||
}
|
||||
}
|
||||
134
crates/guardian_core/src/dcs/text.rs
Normal file
134
crates/guardian_core/src/dcs/text.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
use bevy::{
|
||||
app::{Plugin, PostUpdate},
|
||||
ecs::{
|
||||
component::Component,
|
||||
entity::Entity,
|
||||
query::Added,
|
||||
reflect::ReflectComponent,
|
||||
system::{Commands, Query, Res},
|
||||
},
|
||||
reflect::{std_traits::ReflectDefault, Reflect},
|
||||
utils::AHasher,
|
||||
};
|
||||
use dcs_grpc::dcs::{
|
||||
common::v0::Coalition,
|
||||
net::v0::{net_service_client::NetServiceClient, SendChatRequest},
|
||||
};
|
||||
|
||||
use crate::{GrpcBaseUrl, TokioResource};
|
||||
|
||||
pub struct TextPlugin;
|
||||
|
||||
impl Plugin for TextPlugin {
|
||||
fn build(&self, app: &mut bevy::prelude::App) {
|
||||
app.add_systems(PostUpdate, send_text_message);
|
||||
}
|
||||
}
|
||||
|
||||
fn send_text_message(
|
||||
mut commands: Commands,
|
||||
query: Query<(Entity, &TextMessage), Added<TextMessage>>,
|
||||
url: Res<GrpcBaseUrl>,
|
||||
tokio: Res<TokioResource>,
|
||||
) {
|
||||
let url = url.to_string();
|
||||
|
||||
for (ent, msg) in query.iter() {
|
||||
if msg.as_str().is_empty() {
|
||||
commands.entity(ent).remove::<TextMessage>().despawn();
|
||||
continue;
|
||||
}
|
||||
|
||||
let message = msg.clone();
|
||||
let url = url.clone();
|
||||
tokio.0.spawn(async move {
|
||||
let Ok(mut client) = NetServiceClient::connect(url.to_string()).await else {
|
||||
return;
|
||||
};
|
||||
|
||||
let request = SendChatRequest {
|
||||
message: message.to_string(),
|
||||
coalition: Coalition::All as i32,
|
||||
// target_player_id: player_id,
|
||||
};
|
||||
|
||||
client.send_chat(request).await.ok();
|
||||
});
|
||||
|
||||
commands.entity(ent).remove::<TextMessage>().despawn();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Reflect, Component, Clone)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
pub struct TextMessage {
|
||||
hash: u64,
|
||||
message: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl Default for TextMessage {
|
||||
fn default() -> Self {
|
||||
TextMessage::new("")
|
||||
}
|
||||
}
|
||||
|
||||
impl TextMessage {
|
||||
/// Creates a new [`TextMessage`] from any string-like type.
|
||||
///
|
||||
/// The internal hash will be computed immediately.
|
||||
pub fn new(message: impl Into<Cow<'static, str>>) -> Self {
|
||||
let message = message.into();
|
||||
let mut message = TextMessage { message, hash: 0 };
|
||||
message.update_hash();
|
||||
message
|
||||
}
|
||||
|
||||
/// Sets the entity's message.
|
||||
///
|
||||
/// The internal hash will be re-computed.
|
||||
#[inline(always)]
|
||||
pub fn set(&mut self, message: impl Into<Cow<'static, str>>) {
|
||||
*self = TextMessage::new(message);
|
||||
}
|
||||
|
||||
/// Updates the message of the entity in place.
|
||||
///
|
||||
/// This will allocate a new string if the message was previously
|
||||
/// created from a borrow.
|
||||
#[inline(always)]
|
||||
pub fn mutate<F: FnOnce(&mut String)>(&mut self, f: F) {
|
||||
f(self.message.to_mut());
|
||||
self.update_hash();
|
||||
}
|
||||
|
||||
/// Gets the message of the entity as a `&str`.
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
|
||||
fn update_hash(&mut self) {
|
||||
let mut hasher = AHasher::default();
|
||||
self.message.hash(&mut hasher);
|
||||
self.hash = hasher.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TextMessage {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(&self.message, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for TextMessage {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(&self.message, f)
|
||||
}
|
||||
}
|
||||
92
crates/guardian_core/src/dcs/voice.rs
Normal file
92
crates/guardian_core/src/dcs/voice.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
use bevy::{
|
||||
app::Plugin,
|
||||
ecs::{component::Component, reflect::ReflectComponent},
|
||||
reflect::{std_traits::ReflectDefault, Reflect},
|
||||
utils::AHasher,
|
||||
};
|
||||
|
||||
pub struct SrsPlugin;
|
||||
|
||||
impl Plugin for SrsPlugin {
|
||||
fn build(&self, app: &mut bevy::prelude::App) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_srs() {
|
||||
// will emit strings as commands
|
||||
}
|
||||
|
||||
#[derive(Reflect, Component, Clone)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
pub struct VoiceMessage {
|
||||
hash: u64,
|
||||
message: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl Default for VoiceMessage {
|
||||
fn default() -> Self {
|
||||
VoiceMessage::new("")
|
||||
}
|
||||
}
|
||||
|
||||
impl VoiceMessage {
|
||||
/// Creates a new [`VoiceMessage`] from any string-like type.
|
||||
///
|
||||
/// The internal hash will be computed immediately.
|
||||
pub fn new(message: impl Into<Cow<'static, str>>) -> Self {
|
||||
let message = message.into();
|
||||
let mut message = VoiceMessage { message, hash: 0 };
|
||||
message.update_hash();
|
||||
message
|
||||
}
|
||||
|
||||
/// Sets the entity's message.
|
||||
///
|
||||
/// The internal hash will be re-computed.
|
||||
#[inline(always)]
|
||||
pub fn set(&mut self, message: impl Into<Cow<'static, str>>) {
|
||||
*self = VoiceMessage::new(message);
|
||||
}
|
||||
|
||||
/// Updates the message of the entity in place.
|
||||
///
|
||||
/// This will allocate a new string if the message was previously
|
||||
/// created from a borrow.
|
||||
#[inline(always)]
|
||||
pub fn mutate<F: FnOnce(&mut String)>(&mut self, f: F) {
|
||||
f(self.message.to_mut());
|
||||
self.update_hash();
|
||||
}
|
||||
|
||||
/// Gets the message of the entity as a `&str`.
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
|
||||
fn update_hash(&mut self) {
|
||||
let mut hasher = AHasher::default();
|
||||
self.message.hash(&mut hasher);
|
||||
self.hash = hasher.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VoiceMessage {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(&self.message, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for VoiceMessage {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(&self.message, f)
|
||||
}
|
||||
}
|
||||
79
crates/guardian_core/src/grpc_base_url.rs
Normal file
79
crates/guardian_core/src/grpc_base_url.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
use bevy::{
|
||||
ecs::{reflect::ReflectResource, system::Resource},
|
||||
reflect::{std_traits::ReflectDefault, Reflect},
|
||||
utils::AHasher,
|
||||
};
|
||||
|
||||
#[derive(Reflect, Resource, Clone)]
|
||||
#[reflect(Resource, Default, Debug)]
|
||||
pub struct GrpcBaseUrl {
|
||||
hash: u64,
|
||||
url: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl Default for GrpcBaseUrl {
|
||||
fn default() -> Self {
|
||||
GrpcBaseUrl::new("http://127.0.0.1:50051/")
|
||||
}
|
||||
}
|
||||
|
||||
impl GrpcBaseUrl {
|
||||
/// Creates a new [`GrpcBaseUrl`] from any string-like type.
|
||||
///
|
||||
/// The internal hash will be computed immediately.
|
||||
pub fn new(url: impl Into<Cow<'static, str>>) -> Self {
|
||||
let url = url.into();
|
||||
let mut url = GrpcBaseUrl { url, hash: 0 };
|
||||
url.update_hash();
|
||||
url
|
||||
}
|
||||
|
||||
/// Sets the entity's url.
|
||||
///
|
||||
/// The internal hash will be re-computed.
|
||||
#[inline(always)]
|
||||
pub fn set(&mut self, url: impl Into<Cow<'static, str>>) {
|
||||
*self = GrpcBaseUrl::new(url);
|
||||
}
|
||||
|
||||
/// Updates the url of the entity in place.
|
||||
///
|
||||
/// This will allocate a new string if the url was previously
|
||||
/// created from a borrow.
|
||||
#[inline(always)]
|
||||
pub fn mutate<F: FnOnce(&mut String)>(&mut self, f: F) {
|
||||
f(self.url.to_mut());
|
||||
self.update_hash();
|
||||
}
|
||||
|
||||
/// Gets the url of the entity as a `&str`.
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.url
|
||||
}
|
||||
|
||||
fn update_hash(&mut self) {
|
||||
let mut hasher = AHasher::default();
|
||||
self.url.hash(&mut hasher);
|
||||
self.hash = hasher.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GrpcBaseUrl {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(&self.url, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for GrpcBaseUrl {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(&self.url, f)
|
||||
}
|
||||
}
|
||||
24
crates/guardian_core/src/lib.rs
Normal file
24
crates/guardian_core/src/lib.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
mod dcs;
|
||||
mod grpc_base_url;
|
||||
|
||||
pub use dcs::*;
|
||||
pub use grpc_base_url::*;
|
||||
|
||||
use bevy::app::PluginGroup;
|
||||
use bevy::app::PluginGroupBuilder;
|
||||
use bevy::ecs::system::Resource;
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
pub struct DefaultPlugins;
|
||||
|
||||
impl PluginGroup for DefaultPlugins {
|
||||
fn build(self) -> PluginGroupBuilder {
|
||||
#[allow(unused_mut)]
|
||||
let mut group = PluginGroupBuilder::start::<Self>().add(DcsPlugin);
|
||||
|
||||
group
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct TokioResource(pub Handle);
|
||||
Reference in New Issue
Block a user