Skip to content

Commit

Permalink
Merge pull request #67 from TheZoq2/srsrs
Browse files Browse the repository at this point in the history
Update the srs crate to make it more useable as a full fledged srs client
  • Loading branch information
rkusa authored Jan 6, 2021
2 parents e3eb3e7 + fb01793 commit c6039ed
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 17 deletions.
4 changes: 2 additions & 2 deletions crates/datis-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use futures::future::FutureExt;
use futures::select;
use futures::sink::SinkExt;
use futures::stream::{SplitSink, StreamExt};
use srs::{Client, VoiceStream};
use srs::{Client, VoiceStream, message::Coalition};
use tokio::runtime::{self, Runtime};
use tokio::sync::{oneshot, RwLock};
use tokio::time::delay_for;
Expand Down Expand Up @@ -247,7 +247,7 @@ async fn run(
shutdown_signal: oneshot::Receiver<()>,
) -> Result<(), anyhow::Error> {
let name = format!("ATIS {}", station.name);
let mut client = Client::new(&name, station.freq);
let mut client = Client::new(&name, station.freq, Coalition::Blue);
match &station.transmitter {
#[cfg(feature = "rpc")]
Transmitter::Airfield(airfield) => {
Expand Down
4 changes: 2 additions & 2 deletions crates/radio-station/src/radio_station.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use futures::sink::SinkExt;
use futures::stream::{SplitSink, SplitStream, StreamExt as FutStreamExt};
use ogg::reading::PacketReader;
use ogg_metadata::{AudioMetadata, OggFormat};
use srs::message::LatLngPosition;
use srs::message::{Coalition, LatLngPosition};
use srs::{Client, VoiceStream};
use tokio::sync::oneshot;
use tokio::time::delay_for;
Expand Down Expand Up @@ -50,7 +50,7 @@ impl RadioStation {
path: P,
should_loop: bool,
) -> Result<(), anyhow::Error> {
let mut client = Client::new(&self.name, self.freq);
let mut client = Client::new(&self.name, self.freq, Coalition::Blue);
client.set_position(self.position).await;

let (_tx, rx) = oneshot::channel();
Expand Down
7 changes: 5 additions & 2 deletions crates/srs/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::net::SocketAddr;
use std::sync::Arc;

use crate::message::{create_sguid, GameMessage, LatLngPosition};
use crate::message::{create_sguid, GameMessage, LatLngPosition, Coalition};
use crate::voice_stream::{VoiceStream, VoiceStreamError};

use futures::channel::mpsc;
use tokio::sync::oneshot::Receiver;
use tokio::sync::RwLock;
Expand All @@ -20,16 +21,18 @@ pub struct Client {
freq: u64,
pos: Arc<RwLock<LatLngPosition>>,
unit: Option<UnitInfo>,
pub coalition: Coalition
}

impl Client {
pub fn new(name: &str, freq: u64) -> Self {
pub fn new(name: &str, freq: u64, coalition: Coalition) -> Self {
Client {
sguid: create_sguid(),
name: name.to_string(),
freq,
pos: Arc::new(RwLock::new(LatLngPosition::default())),
unit: None,
coalition
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/srs/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,12 @@ pub struct Message {
}

/// Data received from the in-game srs-plugin.
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GameMessage {
pub control: i32,
pub control: Option<i32>,
pub name: String,
pub lat_lng_position: LatLngPosition,
pub lat_lng: LatLngPosition,
pub ptt: bool,
pub radios: Vec<Radio>,
pub selected: i16,
Expand Down
37 changes: 35 additions & 2 deletions crates/srs/src/voice_codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl Decoder for VoiceCodec {

assert_eq!(
len,
4 + len_audio_part as u64 + len_frequencies + 4 + 8 + 22
4 + len_audio_part as u64 + len_frequencies + 4 + 8 + 1 + 22 + 22
);

let mut audio_part = vec![0u8; len_audio_part as usize];
Expand Down Expand Up @@ -162,8 +162,41 @@ impl Encoder<Packet> for VoiceCodec {
Packet::Voice(packet) => packet,
};

// Packet format as specified in
// https://github.com/ciribob/DCS-SimpleRadioStandalone/blob/1.9.3.0/DCS-SR-Common/Network/UDPVoicePacket.cs#L9
/*
* UDP PACKET LAYOUT
*
* - HEADER SEGMENT
* UInt16 Packet Length - 2 bytes
* UInt16 AudioPart1 Length - 2 bytes
* UInt16 FrequencyPart Length - 2 bytes
* - AUDIO SEGMENT
* Bytes AudioPart1 - variable bytes
* - FREQUENCY SEGMENT (one or multiple)
* double Frequency - 8 bytes
* byte Modulation - 1 byte
* byte Encryption - 1 byte
* - FIXED SEGMENT
* UInt UnitId - 4 bytes
* UInt64 PacketId - 8 bytes
* byte Retransmit / node / hop count - 1 byte
* Bytes / ASCII String TRANSMISSION GUID - 22 bytes used for transmission relay
* Bytes / ASCII String CLIENT GUID - 22 bytes
*/

// NOTE: the final packet will start with the total packet length, but this will be added
// by the inner fixed codec
let header_length = 2 + 2;
let frequency_length = 8 + 1 + 1;
let audio_length = packet.audio_part.len();
let fixed_segment_length = 4 + 8 + 1 + 22 + 22;

let capacity =
4 + packet.audio_part.len() + packet.frequencies.len() * 10 + 4 + 8 + 1 + 22 + 22;
header_length +
audio_length +
frequency_length * packet.frequencies.len()
+ fixed_segment_length;
let mut wd = Cursor::new(Vec::with_capacity(capacity));

// header segment will be written at the end
Expand Down
42 changes: 36 additions & 6 deletions crates/srs/src/voice_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::time::Duration;

use crate::client::Client;
use crate::message::{
Client as MsgClient, Coalition, GameMessage, Message, MsgType, Radio, RadioInfo,
Client as MsgClient, GameMessage, Message, MsgType, Radio, RadioInfo,
RadioSwitchControls,
};
use crate::messages_codec::{self, MessagesCodec};
Expand All @@ -26,6 +26,7 @@ use tokio::time;
use tokio_util::codec::{FramedRead, FramedWrite};
use tokio_util::udp::UdpFramed;

use std::default::Default;
const SRS_VERSION: &str = "1.9.0.0";

pub struct VoiceStream {
Expand Down Expand Up @@ -297,13 +298,42 @@ impl Sink<Vec<u8>> for VoiceStream {
}
}

impl Sink<VoicePacket> for VoiceStream {
type Error = mpsc::SendError;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
let s = self.get_mut();
Pin::new(&mut s.voice_sink).poll_ready(cx)
}

fn start_send(self: Pin<&mut Self>, mut packet: VoicePacket) -> Result<(), Self::Error> {
let mut sguid = [0; 22];
sguid.clone_from_slice(self.client.sguid().as_bytes());
packet.client_sguid = sguid;

let s = self.get_mut();
s.packet_id = s.packet_id.wrapping_add(1);

Pin::new(&mut s.voice_sink).start_send(packet.into())
}

fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
let s = self.get_mut();
Pin::new(&mut s.voice_sink).poll_flush(cx)
}

fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
let s = self.get_mut();
Pin::new(&mut s.voice_sink).poll_close(cx)
}
}

async fn create_radio_update_message(client: &Client) -> Message {
let pos = client.position().await;
Message {
client: Some(MsgClient {
client_guid: client.sguid().to_string(),
name: Some(client.name().to_string()),
coalition: Coalition::Blue,
coalition: client.coalition,
radio_info: Some(RadioInfo {
name: "DATIS Radios".to_string(),
ptt: false,
Expand Down Expand Up @@ -332,7 +362,7 @@ async fn create_update_message(client: &Client) -> Message {
client: Some(MsgClient {
client_guid: client.sguid().to_string(),
name: Some(client.name().to_string()),
coalition: Coalition::Blue,
coalition: client.coalition,
radio_info: None,
lat_lng_position: Some(pos.clone()),
}),
Expand All @@ -348,7 +378,7 @@ async fn create_sync_message(client: &Client) -> Message {
client: Some(MsgClient {
client_guid: client.sguid().to_string(),
name: Some(client.name().to_string()),
coalition: Coalition::Blue,
coalition: client.coalition,
radio_info: None,
lat_lng_position: Some(pos.clone()),
}),
Expand All @@ -359,13 +389,13 @@ async fn create_sync_message(client: &Client) -> Message {
}

fn radio_message_from_game(client: &Client, game_message: &GameMessage) -> Message {
let pos = game_message.lat_lng_position.clone();
let pos = game_message.lat_lng.clone();

Message {
client: Some(MsgClient {
client_guid: client.sguid().to_string(),
name: Some(game_message.name.clone()),
coalition: Coalition::Blue,
coalition: client.coalition,
radio_info: Some(RadioInfo {
name: game_message.name.clone(),
ptt: game_message.ptt,
Expand Down

0 comments on commit c6039ed

Please sign in to comment.