279 lines
10 KiB
Rust
279 lines
10 KiB
Rust
// 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<_>>()
|
|
// }
|
|
// }
|