// 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, // pub callsign: String, // pub group_name: Option, // 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>>, // } // 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 { // // 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 { // // 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 { // 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 { // 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::>() // } // }