Initial Commit
This commit is contained in:
338
crates/guardian_core/src/srs/message.rs
Normal file
338
crates/guardian_core/src/srs/message.rs
Normal file
@@ -0,0 +1,338 @@
|
||||
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(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
#[derive(Default)]
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user