Skip to content

Commit

Permalink
GH-85: Add support for new mumble proto
Browse files Browse the repository at this point in the history
- Add timestamp and uid to text messages
- Add multi profile option
- Add compression for audio
  • Loading branch information
SetZero committed Jan 27, 2024
1 parent 5ade74f commit ea5be5d
Show file tree
Hide file tree
Showing 31 changed files with 396 additions and 113 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,5 @@ dist-ssr

src-tauri/data
src-tauri/src/proto/Mumble.proto
src-tauri/src/proto/MumbleUDP.proto
src-tauri/gen
6 changes: 5 additions & 1 deletion public/locales/en/audio.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@
"Audio deactivation at": "Audio deactivation at {{threshold}}",
"Amplification dB": "Amplification +{{amplification}}dB",
"Echo Cancelation": "Echo Cancelation",
"Noise Suppression": "Noise Suppression"
"Noise Suppression": "Noise Suppression",
"Compressor Threshold": "Compressor Threshold {{threshold}}dB",
"Compressor Ratio": "Compressor Ratio {{ratio}}:1",
"Attack Time": "Attack Time {{duration}}",
"Release Time": "Release Time {{duration}}"
}
1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ scraper = "0.18.1"
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
symphonia = "0.5.3"
mime_guess = "2.0.4"
uuid = "1.7.0"

[dev-dependencies]
tempfile = "3.5.0"
Expand Down
18 changes: 16 additions & 2 deletions src-tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ const MUMBLE_PROTO_SHA256: &str =
"0f86d85938ff2268e3eb05ce0120805fb049ad0d062f4d01c6657b048dcc9245";
const PATCHED_MUMBLE_PROTO_HASH: &str =
"ebadea7bcb720da05149076b1b0ec7a9ff1107a5107a4137b75e8e45fb52f68d";
const DOWNLOAD_MUMBLE_UDP_PROTO_DIR: &str =
"https://raw.githubusercontent.com/mumble-voip/mumble/6a48c0478477054b4e7356b0bd7dc9da24cf0880/src/MumbleUDP.proto";
const MUMBLE_UDP_PROTO_SHA256: &str =
"8087983b0d9a12e11380cad99870a0ef3cee7550b13a114a733aa835acd3d040";

fn apply(diff: Patch, old: &str) -> String {
let old_lines = old.lines().collect::<Vec<&str>>();
Expand Down Expand Up @@ -81,6 +85,7 @@ async fn download_file(

fn main() -> io::Result<()> {
let mumble_proto = Path::new("src/proto/Mumble.proto");
let mumble_udp_proto = Path::new("src/proto/MumbleUDP.proto");
let patch_file = Path::new("src/proto/Mumble.proto.patch");

let mumble_proto_bytes = read_file_as_bytes(mumble_proto).unwrap_or_default();
Expand All @@ -90,20 +95,29 @@ fn main() -> io::Result<()> {
if hash != PATCHED_MUMBLE_PROTO_HASH {
let rt = Runtime::new()?;
rt.block_on(async {
let resonse_file =
let response_file =
download_file(DOWNLOAD_MUMBLE_PROTO_DIR, MUMBLE_PROTO_SHA256, mumble_proto)
.await
.expect("Failed to download Mumble.proto");
let response_str = str::from_utf8(&resonse_file).expect("Failed to parse response");
let response_str = str::from_utf8(&response_file).expect("Failed to parse response");

let patch_output = read_file_as_bytes(patch_file).expect("Failed to read file");
let patch = Patch::from_single(patch_output.as_str()).expect("Failed to parse patch");
let new_content = apply(patch, response_str);
write_to_file(new_content.as_bytes(), mumble_proto);

download_file(
DOWNLOAD_MUMBLE_UDP_PROTO_DIR,
MUMBLE_UDP_PROTO_SHA256,
mumble_udp_proto,
)
.await
.expect("Failed to download MumbleUDP.proto");
});
}

prost_build::compile_protos(&["src/proto/Mumble.proto"], &["src/"])?;
prost_build::compile_protos(&["src/proto/MumbleUDP.proto"], &["src/"])?;
tauri_build::build();

Ok(())
Expand Down
3 changes: 2 additions & 1 deletion src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,12 @@ pub async fn logout(state: State<'_, ConnectionState>) -> Result<(), String> {
#[tauri::command]
pub async fn like_message(
message_id: String,
reciever: Vec<u32>,
state: State<'_, ConnectionState>,
) -> Result<(), String> {
let guard = state.connection.lock().await;
if let Some(guard) = guard.as_ref() {
if let Err(e) = guard.like_message(&message_id) {
if let Err(e) = guard.like_message(&message_id, reciever) {
return Err(format!("{e:?}"));
}
}
Expand Down
18 changes: 10 additions & 8 deletions src-tauri/src/commands/settings_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ use std::{
sync::RwLock,
};

use reqwest::Identity;
use tauri::State;
use tracing::{info, trace};

use crate::{utils::{constants::get_project_dirs, server::{Server, UserIdentity}}, errors::certificate_error::CertificateError};
use crate::{
errors::certificate_error::CertificateError,
utils::{constants::get_project_dirs, server::Server},
};

use super::utils::settings::FrontendSettings;

Expand Down Expand Up @@ -168,24 +170,24 @@ pub fn get_frontend_settings(
Ok(settings_data)
}


#[tauri::command]
pub fn get_identity_certs() -> Result<Vec<String>, String> {
let project_dirs = get_project_dirs()
.ok_or_else(|| CertificateError::new("Unable to load project dir")).map_err(|e| format!("{:?}", e))?;
.ok_or_else(|| CertificateError::new("Unable to load project dir"))
.map_err(|e| format!("{e:?}"))?;
let data_dir = project_dirs.data_dir();

if !data_dir.exists() {
std::fs::create_dir_all(&data_dir).map_err(|e| format!("{:?}", e))?;
std::fs::create_dir_all(data_dir).map_err(|e| format!("{e:?}"))?;
}

let mut certs = Vec::new();

let dir_entries = fs::read_dir(&data_dir)
.map_err(|e| format!("Error reading directory: {}", e))?;
let dir_entries =
fs::read_dir(data_dir).map_err(|e| format!("Error reading directory: {e}"))?;

for entry in dir_entries {
let entry = entry.map_err(|e| format!("Error reading directory entry: {}", e))?;
let entry = entry.map_err(|e| format!("Error reading directory entry: {e}"))?;
let file_name = entry.file_name();
let file_name_str = file_name.to_string_lossy();

Expand Down
9 changes: 9 additions & 0 deletions src-tauri/src/commands/utils/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ pub struct VoiceActivationOptions {
pub voice_hysteresis_upper_threshold: f32,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CompressorOptions {
pub attack_time: usize,
pub release_time: usize,
pub threshold: f32,
pub ratio: f32,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub enum InputMode {
VoiceActivation = 0,
Expand All @@ -42,6 +50,7 @@ pub struct AudioOptions {
pub amplification: f32,
pub input_mode: InputMode,
pub voice_activation_options: Option<VoiceActivationOptions>,
pub compressor_options: Option<CompressorOptions>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand Down
7 changes: 4 additions & 3 deletions src-tauri/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use self::threads::ConnectionThread;

const QUEUE_SIZE: usize = 256;
const BUFFER_SIZE: usize = 8192;
const FANCY_MUMBLE_DATA_ID: &str = "fancy_mumble";

struct ServerData {
username: String,
Expand Down Expand Up @@ -162,12 +163,12 @@ impl Connection {
}

//TODO: Move to output Thread
pub fn like_message(&self, message_id: &str) -> AnyError<()> {
pub fn like_message(&self, message_id: &str, reciever: Vec<u32>) -> AnyError<()> {
let like_message = mumble::proto::PluginDataTransmission {
sender_session: None,
receiver_sessions: Vec::new(),
receiver_sessions: reciever,
data: Some(message_id.as_bytes().to_vec()),
data_id: None,
data_id: Some(FANCY_MUMBLE_DATA_ID.to_owned()),
};
self.tx_out.send(message_builder(&like_message)?)?;

Expand Down
1 change: 0 additions & 1 deletion src-tauri/src/connection/threads/input_thread.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::sync::atomic::Ordering;

use crate::connection::Connection;
use crate::connection::traits::Shutdown;
use crate::protocol::message_router::MessageRouter;
use crate::protocol::stream_reader::StreamReader;
use tokio::select;
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/connection/threads/output_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ impl OutputThread for Connection {
channel_id: result.channel_id.iter().copied().collect(),
tree_id: Vec::new(),
message: result.message,
message_id: None,
timestamp: None,
};
trace!("Sending message: {:?}", message);
let buffer = message_builder(&message).unwrap_or_default();
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/connection/threads/ping_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl PingThread for Connection {
deadman_counter += 1;
if deadman_counter > MAX_PING_FAILURES {
let message = FrontendMessage::new("ping_timeout", "Timeout while sending Ping");
send_to_frontend(&frontend_channel, &message)
send_to_frontend(&frontend_channel, &message);
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/manager/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
};

use serde::Serialize;
use tracing::{debug, error, info};
use tracing::{debug, info};

use crate::{
errors::AnyError, mumble, protocol::serialize::message_container::FrontendMessage,
Expand Down
3 changes: 0 additions & 3 deletions src-tauri/src/manager/connection_state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
use serde::Serialize;
use tracing::error;

use crate::{protocol::serialize::message_container::FrontendMessage, utils::frontend::send_to_frontend};

use tokio::sync::broadcast::Sender;
Expand Down
19 changes: 15 additions & 4 deletions src-tauri/src/manager/text_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde::Serialize;
use tokio::sync::broadcast::Sender;
use tracing::error;

use crate::{errors::AnyError, mumble, protocol::serialize::message_container::FrontendMessage};
use crate::{mumble, protocol::serialize::message_container::FrontendMessage};

use super::user::User;

Expand All @@ -19,6 +19,7 @@ struct TextMessage {
sender: SenderInfo,
message: String,
timestamp: u128,
id: Option<String>,
}

pub struct Manager {
Expand Down Expand Up @@ -68,17 +69,27 @@ impl Manager {
&mut self,
text_message: mumble::proto::TextMessage,
user: &User,
) -> AnyError<()> {
) {
let timestamp = text_message.timestamp.map_or_else(
|| {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis()
},
u128::from,
);

let message = TextMessage {
sender: SenderInfo {
user_id: user.id,
user_name: user.name.clone(),
},
message: text_message.message,
timestamp: SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis(),
timestamp,
id: text_message.message_id,
};
self.message_log.push(message);
self.notify_last();
Ok(())
}
}
31 changes: 15 additions & 16 deletions src-tauri/src/manager/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ use base64::{engine::general_purpose, Engine as _};
use std::collections::{hash_map::Entry, HashMap};

use serde::{Deserialize, Serialize};
use tracing::{debug, error, info, trace, warn};
use tracing::{debug, info, trace};

use crate::{
errors::AnyError,
mumble,
protocol::serialize::message_container::FrontendMessage,
utils::{
file::{read_data_from_cache, store_data_in_cache},
messages::message_builder, frontend::send_to_frontend,
frontend::send_to_frontend,
messages::message_builder,
},
};

Expand Down Expand Up @@ -66,10 +67,10 @@ pub struct UpdateableUserState {

#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct SyncInfo {
pub session: Option<u32>,
pub max_bandwidth: Option<u32>,
pub welcome_text: Option<String>,
pub permissions: Option<u64>,
pub session: Option<u32>,
pub max_bandwidth: Option<u32>,
pub welcome_text: Option<String>,
pub permissions: Option<u64>,
}

#[derive(Debug, Default, Serialize)]
Expand Down Expand Up @@ -100,7 +101,6 @@ impl Update<mumble::proto::UserState> for User {

pub struct Manager {
users: HashMap<u32, User>,
current_user_id: Option<u32>,
frontend_channel: Sender<String>,
server_channel: Sender<Vec<u8>>,
}
Expand All @@ -109,7 +109,6 @@ impl Manager {
pub fn new(send_to: Sender<String>, server_channel: Sender<Vec<u8>>) -> Self {
Self {
users: HashMap::new(),
current_user_id: None,
frontend_channel: send_to,
server_channel,
}
Expand Down Expand Up @@ -320,14 +319,14 @@ impl Manager {
}

pub fn notify_current_user(&mut self, sync_info: &mumble::proto::ServerSync) {
let sync_info = SyncInfo {
session: sync_info.session,
max_bandwidth: sync_info.max_bandwidth,
welcome_text: sync_info.welcome_text.clone(),
permissions: sync_info.permissions,
};
let message = FrontendMessage::new("sync_info", sync_info);
send_to_frontend(&self.frontend_channel, &message);
let sync_info = SyncInfo {
session: sync_info.session,
max_bandwidth: sync_info.max_bandwidth,
welcome_text: sync_info.welcome_text.clone(),
permissions: sync_info.permissions,
};
let message = FrontendMessage::new("sync_info", sync_info);
send_to_frontend(&self.frontend_channel, &message);
}
}

Expand Down
11 changes: 8 additions & 3 deletions src-tauri/src/manager/voice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use async_trait::async_trait;
use serde::Serialize;
use std::collections::{hash_map::Entry, HashMap};
use tokio::sync::broadcast::{Receiver, Sender};
use tracing::error;

const SAMPLE_RATE: u32 = 48000;
const CHANNELS: opus::Channels = opus::Channels::Mono;
Expand All @@ -30,7 +29,7 @@ pub struct Manager {
user_audio_info: HashMap<u32, AudioInfo>,
audio_player: Player,
recoder: Recorder,
decoder: audio::decoder::Decoder,
decoder: Box<dyn audio::decoder::Decoder>,
}

impl Manager {
Expand Down Expand Up @@ -63,7 +62,7 @@ impl Manager {
user_audio_info: HashMap::new(),
audio_player: player,
recoder,
decoder: audio::decoder::Decoder::new(SAMPLE_RATE, CHANNELS),
decoder: Box::new(audio::decoder::UDPDecoder::new(SAMPLE_RATE, CHANNELS)),
})
}

Expand Down Expand Up @@ -118,6 +117,12 @@ impl Manager {

Ok(())
}

pub(crate) fn set_codec(&self, codec_version: &mumble::proto::CodecVersion) {
send_to_frontend(
&self.frontend_channel,
&FrontendMessage::new("set_codec", format!("{codec_version:?}")));
}
}

#[async_trait]
Expand Down
Loading

0 comments on commit ea5be5d

Please sign in to comment.