Files
guardian/crates/guardian_core/src/srs/message.rs

338 lines
8.5 KiB
Rust
Executable File

use base64::{
alphabet::URL_SAFE,
engine::{general_purpose::NO_PAD, GeneralPurpose},
Engine,
};
use bevy::ecs::component::Component;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::{
collections::HashMap,
error::Error,
fmt::{self, Display},
};
use uuid::Uuid;
pub(crate) const BASE64: GeneralPurpose = base64::engine::GeneralPurpose::new(&URL_SAFE, NO_PAD);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase", untagged)]
pub enum Message {
Update(UpdateMessage),
Ping(PingMessage),
Sync(SyncMessage),
RadioUpdate(RadioUpdateMessage),
ServerSettings(ServerSettingsMessage),
ClientDisconnect(ClientDisconnectMessage),
VersionMismatch(VersionMismatchMessage),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase", untagged)]
pub enum MessageRequest {
Update(UpdateMessage),
Sync(SyncMessageRequest),
RadioUpdate(RadioUpdateMessage),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct UpdateMessage {
pub msg_type: MsgType<0>,
pub client: Client,
pub version: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct PingMessage {
pub msg_type: MsgType<1>,
pub version: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SyncMessage {
pub msg_type: MsgType<2>,
pub clients: Vec<Client>,
pub server_settings: HashMap<String, String>,
pub version: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SyncMessageRequest {
pub msg_type: MsgType<2>,
pub client: Client,
pub version: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct RadioUpdateMessage {
pub msg_type: MsgType<3>,
pub client: Client,
pub version: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ServerSettingsMessage {
pub msg_type: MsgType<4>,
pub server_settings: HashMap<String, String>,
pub version: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ClientDisconnectMessage {
pub msg_type: MsgType<5>,
pub client: Client,
pub version: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct VersionMismatchMessage {
pub msg_type: MsgType<6>,
pub version: String,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Coalition {
Spectator,
Blue,
Red,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Radio {
pub enc: bool,
pub enc_key: u8,
pub freq: f64,
pub modulation: Modulation,
pub sec_freq: f64,
pub retransmit: bool,
}
impl Default for Radio {
fn default() -> Self {
Radio {
enc: false,
enc_key: 0,
freq: 1.0,
modulation: Modulation::Disabled,
sec_freq: 1.0,
retransmit: false,
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum Modulation {
Am = 0,
Fm = 1,
Intercom = 2,
#[default]
Disabled = 3,
HaveQuick = 4,
Satcom = 5,
Mids = 6,
}
#[derive(Component, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RadioInfo {
pub radios: Vec<Radio>,
pub unit: String,
pub unit_id: u32,
pub iff: Transponder,
}
#[derive(Debug, PartialEq, Default, Clone, Copy, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum RadioSwitchControls {
#[default]
Hotas = 0,
InCockpit = 1,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Client {
pub client_guid: String,
pub name: String,
pub seat: u32,
pub coalition: Coalition,
pub allow_record: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub radio_info: Option<RadioInfo>,
pub lat_lng_position: Position,
}
#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize)]
pub struct Position {
pub lat: f64,
#[serde(rename = "lng")]
pub lon: f64,
pub alt: f64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transponder {
control: IffControlMode,
mode1: i32,
mode3: i32,
mode4: bool,
mic: i32,
status: IffStatus,
}
#[derive(Debug, Serialize_repr, Deserialize_repr, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum IffControlMode {
Cockpit = 0,
Overlay = 1,
Disabled = 2,
}
#[derive(Debug, Serialize_repr, Deserialize_repr, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum IffStatus {
Off = 0,
Normal = 1,
Ident = 2,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct MsgType<const V: u8>;
#[derive(Debug)]
struct MsgTypeError;
impl Error for MsgTypeError {}
impl Display for MsgTypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Invalid message type")
}
}
impl<const V: u8> Serialize for MsgType<V> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_u8(V)
}
}
impl<'de, const V: u8> Deserialize<'de> for MsgType<V> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = u8::deserialize(deserializer)?;
if value == V {
Ok(MsgType::<V>)
} else {
Err(serde::de::Error::custom(MsgTypeError))
}
}
}
impl Default for Transponder {
fn default() -> Self {
Transponder {
control: IffControlMode::Disabled,
mode1: -1,
mode3: -1,
mode4: false,
mic: -1,
status: IffStatus::Off,
}
}
}
impl ::serde::Serialize for Coalition {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
// Serialize the enum as a u64.
serializer.serialize_u64(match *self {
Coalition::Spectator => 0,
Coalition::Red => 1,
Coalition::Blue => 2,
})
}
}
impl<'de> ::serde::Deserialize<'de> for Coalition {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
struct Visitor;
impl<'de> ::serde::de::Visitor<'de> for Visitor {
type Value = Coalition;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("positive integer or string")
}
fn visit_u64<E>(self, value: u64) -> Result<Coalition, E>
where
E: ::serde::de::Error,
{
// Rust does not come with a simple way of converting a
// number to an enum, so use a big `match`.
match value {
0 => Ok(Coalition::Spectator),
1 => Ok(Coalition::Red),
2 => Ok(Coalition::Blue),
_ => Err(E::custom(format!(
"unknown {} value: {}",
stringify!(Coalition),
value
))),
}
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let value = value.to_lowercase().clone();
let value = value.as_str();
match value {
"spectator" => Ok(Coalition::Spectator),
"red" => Ok(Coalition::Red),
"blue" => Ok(Coalition::Blue),
_ => Err(E::custom(format!(
"unknown {} value: {}",
stringify!(Coalition),
value
))),
}
}
}
// Deserialize the enum from a u64.
deserializer.deserialize_u64(Visitor)
}
}
pub fn create_sguid() -> String {
let sguid = Uuid::new_v4();
// let sguid = base64::encode_config(sguid.as_bytes(), base64::URL_SAFE_NO_PAD);
let sguid = BASE64.encode(sguid.as_bytes());
assert_eq!(sguid.len(), 22);
sguid
}