diff options
| author | Gustav Sörnäs <gustav@sornas.net> | 2021-03-31 21:51:47 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-31 21:51:47 +0200 |
| commit | 3f6281020b72ba949147a282c18c60a2842ad3dc (patch) | |
| tree | 0ba20ba532d325bf072969013fe8cf5bde84f6ba /mumd/src | |
| parent | 795e46c98616801c678bd0a403b08cb0fcd5ee43 (diff) | |
| parent | 46a3938b6d9d81649e38e6e793599a52991d803d (diff) | |
| download | mum-3f6281020b72ba949147a282c18c60a2842ad3dc.tar.gz | |
Merge pull request #42 from mum-rs/handle-panics
Diffstat (limited to 'mumd/src')
| -rw-r--r-- | mumd/src/audio.rs | 26 | ||||
| -rw-r--r-- | mumd/src/client.rs | 27 | ||||
| -rw-r--r-- | mumd/src/error.rs | 127 | ||||
| -rw-r--r-- | mumd/src/main.rs | 35 | ||||
| -rw-r--r-- | mumd/src/network.rs | 19 | ||||
| -rw-r--r-- | mumd/src/network/tcp.rs | 87 | ||||
| -rw-r--r-- | mumd/src/network/udp.rs | 16 | ||||
| -rw-r--r-- | mumd/src/notifications.rs (renamed from mumd/src/notify.rs) | 10 | ||||
| -rw-r--r-- | mumd/src/state.rs | 26 |
9 files changed, 270 insertions, 103 deletions
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs index 6b46a7a..fdbeaee 100644 --- a/mumd/src/audio.rs +++ b/mumd/src/audio.rs @@ -4,6 +4,7 @@ mod noise_gate; use crate::audio::output::SaturatingAdd; use crate::audio::noise_gate::{from_interleaved_samples_stream, OpusEncoder, StreamingNoiseGate, StreamingSignalExt}; +use crate::error::{AudioError, AudioStream}; use crate::network::VoiceStreamType; use crate::state::StatePhase; @@ -85,16 +86,16 @@ pub struct Audio { } impl Audio { - pub fn new(input_volume: f32, output_volume: f32, phase_watcher: watch::Receiver<StatePhase>) -> Self { + pub fn new(input_volume: f32, output_volume: f32, phase_watcher: watch::Receiver<StatePhase>) -> Result<Self, AudioError> { let sample_rate = SampleRate(SAMPLE_RATE); let host = cpal::default_host(); let output_device = host .default_output_device() - .expect("default output device not found"); + .ok_or(AudioError::NoDevice(AudioStream::Output))?; let output_supported_config = output_device .supported_output_configs() - .expect("error querying output configs") + .map_err(|e| AudioError::NoConfigs(AudioStream::Output, e))? .find_map(|c| { if c.min_sample_rate() <= sample_rate && c.max_sample_rate() >= sample_rate { Some(c) @@ -102,17 +103,17 @@ impl Audio { None } }) - .unwrap() + .ok_or(AudioError::NoSupportedConfig(AudioStream::Output))? .with_sample_rate(sample_rate); let output_supported_sample_format = output_supported_config.sample_format(); let output_config: StreamConfig = output_supported_config.into(); let input_device = host .default_input_device() - .expect("default input device not found"); + .ok_or(AudioError::NoDevice(AudioStream::Input))?; let input_supported_config = input_device .supported_input_configs() - .expect("error querying output configs") + .map_err(|e| AudioError::NoConfigs(AudioStream::Input, e))? .find_map(|c| { if c.min_sample_rate() <= sample_rate && c.max_sample_rate() >= sample_rate { Some(c) @@ -120,7 +121,7 @@ impl Audio { None } }) - .unwrap() + .ok_or(AudioError::NoSupportedConfig(AudioStream::Input))? .with_sample_rate(sample_rate); let input_supported_sample_format = input_supported_config.sample_format(); let input_config: StreamConfig = input_supported_config.into(); @@ -164,7 +165,7 @@ impl Audio { err_fn, ), } - .unwrap(); + .map_err(|e| AudioError::InvalidStream(AudioStream::Output, e))?; let (sample_sender, sample_receiver) = futures_channel::mpsc::channel(1_000_000); @@ -199,7 +200,7 @@ impl Audio { err_fn, ), } - .unwrap(); + .map_err(|e| AudioError::InvalidStream(AudioStream::Input, e))?; let opus_stream = OpusEncoder::new( 4, @@ -217,7 +218,7 @@ impl Audio { position_info: None, }); - output_stream.play().unwrap(); + output_stream.play().map_err(|e| AudioError::OutputPlayError(e))?; let mut res = Self { output_config, @@ -232,7 +233,7 @@ impl Audio { play_sounds, }; res.load_sound_effects(&[]); - res + Ok(res) } pub fn load_sound_effects(&mut self, sound_effects: &[SoundEffect]) { @@ -268,7 +269,7 @@ impl Audio { let iter: Box<dyn Iterator<Item = f32>> = match spec.channels { 1 => Box::new(samples.into_iter().flat_map(|e| vec![e, e])), 2 => Box::new(samples.into_iter()), - _ => unimplemented!() // TODO handle gracefully (this might not even happen) + _ => unimplemented!("Only mono and stereo sound is supported. See #80.") }; let mut signal = signal::from_interleaved_samples_iter::<_, [f32; 2]>(iter); let interp = Linear::new(Signal::next(&mut signal), Signal::next(&mut signal)); @@ -401,4 +402,3 @@ fn get_sfx(file: &str) -> Cow<'static, [u8]> { fn get_default_sfx() -> Cow<'static, [u8]> { Cow::from(include_bytes!("fallback_sfx.wav").as_ref()) } - diff --git a/mumd/src/client.rs b/mumd/src/client.rs index cdae7eb..c1a0152 100644 --- a/mumd/src/client.rs +++ b/mumd/src/client.rs @@ -1,18 +1,21 @@ use crate::command; +use crate::error::ClientError; use crate::network::{tcp, udp, ConnectionInfo}; use crate::state::State; +use futures_util::{select, FutureExt}; use mumble_protocol::{Serverbound, control::ControlPacket, crypt::ClientCryptState}; use mumlib::command::{Command, CommandResponse}; use std::sync::Arc; -use tokio::{join, sync::{Mutex, mpsc, oneshot, watch}}; +use tokio::sync::{Mutex, mpsc, oneshot, watch}; pub async fn handle( + state: State, command_receiver: mpsc::UnboundedReceiver<( Command, oneshot::Sender<mumlib::error::Result<Option<CommandResponse>>>, )>, -) { +) -> Result<(), ClientError> { let (connection_info_sender, connection_info_receiver) = watch::channel::<Option<ConnectionInfo>>(None); let (crypt_state_sender, crypt_state_receiver) = @@ -24,30 +27,30 @@ pub async fn handle( let (response_sender, response_receiver) = mpsc::unbounded_channel(); - let state = State::new(); let state = Arc::new(Mutex::new(state)); - join!( - tcp::handle( + + select! { + r = tcp::handle( Arc::clone(&state), connection_info_receiver.clone(), crypt_state_sender, packet_sender.clone(), packet_receiver, response_receiver, - ), - udp::handle( + ).fuse() => r.map_err(|e| ClientError::TcpError(e)), + _ = udp::handle( Arc::clone(&state), connection_info_receiver.clone(), crypt_state_receiver, - ), - command::handle( + ).fuse() => Ok(()), + _ = command::handle( state, command_receiver, response_sender, ping_request_sender, packet_sender, connection_info_sender, - ), - udp::handle_pings(ping_request_receiver), - ); + ).fuse() => Ok(()), + _ = udp::handle_pings(ping_request_receiver).fuse() => Ok(()), + } } diff --git a/mumd/src/error.rs b/mumd/src/error.rs new file mode 100644 index 0000000..f7818a1 --- /dev/null +++ b/mumd/src/error.rs @@ -0,0 +1,127 @@ +use mumble_protocol::{Serverbound, control::ControlPacket}; +use mumlib::error::ConfigError; +use std::fmt; +use tokio::sync::mpsc; + +pub type ServerSendError = mpsc::error::SendError<ControlPacket<Serverbound>>; + +pub enum TcpError { + NoConnectionInfoReceived, + TlsConnectorBuilderError(native_tls::Error), + TlsConnectError(native_tls::Error), + SendError(ServerSendError), + + IOError(std::io::Error), +} + +impl fmt::Display for TcpError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TcpError::NoConnectionInfoReceived + => write!(f, "No connection info received"), + TcpError::TlsConnectorBuilderError(e) + => write!(f, "Error building TLS connector: {}", e), + TcpError::TlsConnectError(e) + => write!(f, "TLS error when connecting: {}", e), + TcpError::SendError(e) => write!(f, "Couldn't send packet: {}", e), + TcpError::IOError(e) => write!(f, "IO error: {}", e), + } + } +} + +impl From<std::io::Error> for TcpError { + fn from(e: std::io::Error) -> Self { + TcpError::IOError(e) + } +} + +impl From<ServerSendError> for TcpError { + fn from(e: ServerSendError) -> Self { + TcpError::SendError(e) + } +} + +pub enum UdpError { + NoConnectionInfoReceived, + DisconnectBeforeCryptSetup, + + IOError(std::io::Error), +} + +impl From<std::io::Error> for UdpError { + fn from(e: std::io::Error) -> Self { + UdpError::IOError(e) + } +} + +pub enum ClientError { + TcpError(TcpError), +} + +impl fmt::Display for ClientError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ClientError::TcpError(e) => write!(f, "TCP error: {}", e), + } + } +} + +pub enum AudioStream { + Input, + Output, +} + +impl fmt::Display for AudioStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AudioStream::Input => write!(f, "input"), + AudioStream::Output => write!(f, "output"), + } + } +} + +pub enum AudioError { + NoDevice(AudioStream), + NoConfigs(AudioStream, cpal::SupportedStreamConfigsError), + NoSupportedConfig(AudioStream), + InvalidStream(AudioStream, cpal::BuildStreamError), + OutputPlayError(cpal::PlayStreamError), +} + +impl fmt::Display for AudioError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AudioError::NoDevice(s) => write!(f, "No {} device", s), + AudioError::NoConfigs(s, e) => write!(f, "No {} configs: {}", s, e), + AudioError::NoSupportedConfig(s) => write!(f, "No supported {} config found", s), + AudioError::InvalidStream(s, e) => write!(f, "Invalid {} stream: {}", s, e), + AudioError::OutputPlayError(e) => write!(f, "Playback error: {}", e), + } + } +} + +pub enum StateError { + AudioError(AudioError), + ConfigError(ConfigError), +} + +impl From<AudioError> for StateError { + fn from(e: AudioError) -> Self { + StateError::AudioError(e) + } +} + +impl From<ConfigError> for StateError { + fn from(e: ConfigError) -> Self { + StateError::ConfigError(e) + } +} + +impl fmt::Display for StateError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + StateError::AudioError(e) => write!(f, "Audio error: {}", e), + StateError::ConfigError(e) => write!(f, "Config error: {}", e), + } + } +} diff --git a/mumd/src/main.rs b/mumd/src/main.rs index 276e2ce..d7bc2c0 100644 --- a/mumd/src/main.rs +++ b/mumd/src/main.rs @@ -1,15 +1,18 @@ mod audio; mod client; mod command; +mod error; mod network; -mod notify; +mod notifications; mod state; -use futures_util::{SinkExt, StreamExt}; +use crate::state::State; + +use futures_util::{select, FutureExt, SinkExt, StreamExt}; use log::*; use mumlib::command::{Command, CommandResponse}; use mumlib::setup_logger; -use tokio::{join, net::{UnixListener, UnixStream}, sync::{mpsc, oneshot}}; +use tokio::{net::{UnixListener, UnixStream}, sync::{mpsc, oneshot}}; use tokio_util::codec::{FramedRead, FramedWrite, LengthDelimitedCodec}; use bytes::{BufMut, BytesMut}; @@ -21,7 +24,7 @@ async fn main() { } setup_logger(std::io::stderr(), true); - notify::init(); + notifications::init(); // check if another instance is live let connection = UnixStream::connect(mumlib::SOCKET_PATH).await; @@ -53,10 +56,26 @@ async fn main() { let (command_sender, command_receiver) = mpsc::unbounded_channel(); - join!( - client::handle(command_receiver), - receive_commands(command_sender), - ); + let state = match State::new() { + Ok(s) => s, + Err(e) => { + error!("Error instantiating mumd: {}", e); + return; + } + }; + + let run = select! { + r = client::handle(state, command_receiver).fuse() => r, + _ = receive_commands(command_sender).fuse() => Ok(()), + }; + + match run { + Err(e) => { + error!("mumd: {}", e); + std::process::exit(1); + } + _ => {} + } } async fn receive_commands( diff --git a/mumd/src/network.rs b/mumd/src/network.rs index 38a97ce..4eca90d 100644 --- a/mumd/src/network.rs +++ b/mumd/src/network.rs @@ -4,7 +4,7 @@ pub mod udp; use futures_util::FutureExt; use log::*; use std::{future::Future, net::SocketAddr}; -use tokio::{join, select, sync::{oneshot, watch}}; +use tokio::{select, sync::{oneshot, watch}}; use crate::state::StatePhase; @@ -31,12 +31,12 @@ pub enum VoiceStreamType { UDP, } -async fn run_until<F>( +async fn run_until<F, R>( phase_checker: impl Fn(StatePhase) -> bool, fut: F, mut phase_watcher: watch::Receiver<StatePhase>, -) where - F: Future<Output = ()>, +) -> Option<R> + where F: Future<Output = R>, { let (tx, rx) = oneshot::channel(); let phase_transition_block = async { @@ -55,10 +55,13 @@ async fn run_until<F>( let rx = rx.fuse(); let fut = fut.fuse(); select! { - _ = fut => (), - _ = rx => (), - }; + r = fut => Some(r), + _ = rx => None, + } }; - join!(main_block, phase_transition_block); + select! { + m = main_block => m, + _ = phase_transition_block => None, + } } diff --git a/mumd/src/network/tcp.rs b/mumd/src/network/tcp.rs index 47b1c20..6402a89 100644 --- a/mumd/src/network/tcp.rs +++ b/mumd/src/network/tcp.rs @@ -1,8 +1,10 @@ +use crate::error::{ServerSendError, TcpError}; use crate::network::ConnectionInfo; use crate::state::{State, StatePhase}; use log::*; use futures_util::{FutureExt, SinkExt, StreamExt}; +use futures_util::select; use futures_util::stream::{SplitSink, SplitStream, Stream}; use mumble_protocol::control::{msgs, ClientControlCodec, ControlCodec, ControlPacket}; use mumble_protocol::crypt::ClientCryptState; @@ -19,7 +21,6 @@ use tokio_native_tls::{TlsConnector, TlsStream}; use tokio_util::codec::{Decoder, Framed}; use super::{run_until, VoiceStreamType}; -use futures_util::future::join5; type TcpSender = SplitSink< Framed<TlsStream<TcpStream>, ControlCodec<Serverbound, Clientbound>>, @@ -84,7 +85,7 @@ pub async fn handle( packet_sender: mpsc::UnboundedSender<ControlPacket<Serverbound>>, mut packet_receiver: mpsc::UnboundedReceiver<ControlPacket<Serverbound>>, mut tcp_event_register_receiver: mpsc::UnboundedReceiver<(TcpEvent, TcpEventCallback)>, -) { +) -> Result<(), TcpError> { loop { let connection_info = 'data: loop { while connection_info_receiver.changed().await.is_ok() { @@ -92,20 +93,20 @@ pub async fn handle( break 'data data; } } - return; + return Err(TcpError::NoConnectionInfoReceived); }; let (mut sink, stream) = connect( connection_info.socket_addr, connection_info.hostname, connection_info.accept_invalid_cert, ) - .await; + .await?; // Handshake (omitting `Version` message for brevity) let state_lock = state.lock().await; let username = state_lock.username().unwrap().to_string(); let password = state_lock.password().map(|x| x.to_string()); - authenticate(&mut sink, username, password).await; + authenticate(&mut sink, username, password).await?; let phase_watcher = state_lock.phase_receiver(); let input_receiver = state_lock.audio().input_receiver(); drop(state_lock); @@ -113,26 +114,30 @@ pub async fn handle( info!("Logging in..."); + let phase_watcher_inner = phase_watcher.clone(); + run_until( |phase| matches!(phase, StatePhase::Disconnected), - join5( - send_pings(packet_sender.clone(), 10), - listen( - Arc::clone(&state), - stream, - crypt_state_sender.clone(), - event_queue.clone(), - ), - send_voice( - packet_sender.clone(), - Arc::clone(&input_receiver), - phase_watcher.clone(), - ), - send_packets(sink, &mut packet_receiver), - register_events(&mut tcp_event_register_receiver, event_queue.clone()), - ).map(|_| ()), + async { + select! { + r = send_pings(packet_sender.clone(), 10).fuse() => r, + r = listen( + Arc::clone(&state), + stream, + crypt_state_sender.clone(), + event_queue.clone(), + ).fuse() => r, + r = send_voice( + packet_sender.clone(), + Arc::clone(&input_receiver), + phase_watcher_inner, + ).fuse() => r, + r = send_packets(sink, &mut packet_receiver).fuse() => r, + _ = register_events(&mut tcp_event_register_receiver, event_queue.clone()).fuse() => Ok(()), + } + }, phase_watcher, - ).await; + ).await.unwrap_or(Ok(()))?; event_queue.resolve(TcpEventData::Disconnected).await; @@ -144,62 +149,62 @@ async fn connect( server_addr: SocketAddr, server_host: String, accept_invalid_cert: bool, -) -> (TcpSender, TcpReceiver) { - let stream = TcpStream::connect(&server_addr) - .await - .expect("failed to connect to server:"); +) -> Result<(TcpSender, TcpReceiver), TcpError> { + let stream = TcpStream::connect(&server_addr).await?; debug!("TCP connected"); let mut builder = native_tls::TlsConnector::builder(); builder.danger_accept_invalid_certs(accept_invalid_cert); let connector: TlsConnector = builder .build() - .expect("failed to create TLS connector") + .map_err(|e| TcpError::TlsConnectorBuilderError(e))? .into(); let tls_stream = connector .connect(&server_host, stream) .await - .expect("failed to connect TLS: {}"); + .map_err(|e| TcpError::TlsConnectError(e))?; debug!("TLS connected"); // Wrap the TLS stream with Mumble's client-side control-channel codec - ClientControlCodec::new().framed(tls_stream).split() + Ok(ClientControlCodec::new().framed(tls_stream).split()) } async fn authenticate( sink: &mut TcpSender, username: String, password: Option<String> -) { +) -> Result<(), TcpError> { let mut msg = msgs::Authenticate::new(); msg.set_username(username); if let Some(password) = password { msg.set_password(password); } msg.set_opus(true); - sink.send(msg.into()).await.unwrap(); + sink.send(msg.into()).await?; + Ok(()) } async fn send_pings( packet_sender: mpsc::UnboundedSender<ControlPacket<Serverbound>>, delay_seconds: u64, -) { +) -> Result<(), TcpError> { let mut interval = time::interval(Duration::from_secs(delay_seconds)); loop { interval.tick().await; trace!("Sending TCP ping"); let msg = msgs::Ping::new(); - packet_sender.send(msg.into()).unwrap(); + packet_sender.send(msg.into())?; } } async fn send_packets( mut sink: TcpSender, packet_receiver: &mut mpsc::UnboundedReceiver<ControlPacket<Serverbound>>, -) { +) -> Result<(), TcpError> { loop { + // Safe since we always have at least one sender alive. let packet = packet_receiver.recv().await.unwrap(); - sink.send(packet).await.unwrap(); + sink.send(packet).await?; } } @@ -207,7 +212,7 @@ async fn send_voice( packet_sender: mpsc::UnboundedSender<ControlPacket<Serverbound>>, receiver: Arc<Mutex<Box<(dyn Stream<Item = VoicePacket<Serverbound>> + Unpin)>>>, phase_watcher: watch::Receiver<StatePhase>, -) { +) -> Result<(), TcpError> { loop { let mut inner_phase_watcher = phase_watcher.clone(); loop { @@ -226,13 +231,12 @@ async fn send_voice( .await .next() .await - .unwrap() - .into()) - .unwrap(); + .expect("No audio stream") + .into())?; } }, inner_phase_watcher.clone(), - ).await; + ).await.unwrap_or(Ok::<(), ServerSendError>(()))?; } } @@ -241,7 +245,7 @@ async fn listen( mut stream: TcpReceiver, crypt_state_sender: mpsc::Sender<ClientCryptState>, event_queue: TcpEventQueue, -) { +) -> Result<(), TcpError> { let mut crypt_state = None; let mut crypt_state_sender = Some(crypt_state_sender); @@ -367,6 +371,7 @@ async fn listen( } } } + Ok(()) } async fn register_events( diff --git a/mumd/src/network/udp.rs b/mumd/src/network/udp.rs index da92dcb..5996e43 100644 --- a/mumd/src/network/udp.rs +++ b/mumd/src/network/udp.rs @@ -1,3 +1,4 @@ +use crate::error::UdpError; use crate::network::ConnectionInfo; use crate::state::{State, StatePhase}; @@ -31,7 +32,7 @@ pub async fn handle( state: Arc<Mutex<State>>, mut connection_info_receiver: watch::Receiver<Option<ConnectionInfo>>, mut crypt_state_receiver: mpsc::Receiver<ClientCryptState>, -) { +) -> Result<(), UdpError> { let receiver = state.lock().await.audio().input_receiver(); loop { @@ -41,9 +42,9 @@ pub async fn handle( break 'data data; } } - return; + return Err(UdpError::NoConnectionInfoReceived); }; - let (sink, source) = connect(&mut crypt_state_receiver).await; + let (sink, source) = connect(&mut crypt_state_receiver).await?; let sink = Arc::new(Mutex::new(sink)); let source = Arc::new(Mutex::new(source)); @@ -82,22 +83,21 @@ pub async fn handle( async fn connect( crypt_state: &mut mpsc::Receiver<ClientCryptState>, -) -> (UdpSender, UdpReceiver) { +) -> Result<(UdpSender, UdpReceiver), UdpError> { // Bind UDP socket let udp_socket = UdpSocket::bind((Ipv6Addr::from(0u128), 0u16)) - .await - .expect("Failed to bind UDP socket"); + .await?; // Wait for initial CryptState let crypt_state = match crypt_state.recv().await { Some(crypt_state) => crypt_state, // disconnected before we received the CryptSetup packet, oh well - None => panic!("Disconnect before crypt packet received"), //TODO exit gracefully + None => return Err(UdpError::DisconnectBeforeCryptSetup), }; debug!("UDP connected"); // Wrap the raw UDP packets in Mumble's crypto and voice codec (CryptState does both) - UdpFramed::new(udp_socket, crypt_state).split() + Ok(UdpFramed::new(udp_socket, crypt_state).split()) } async fn new_crypt_state( diff --git a/mumd/src/notify.rs b/mumd/src/notifications.rs index ee387cc..bccf4dd 100644 --- a/mumd/src/notify.rs +++ b/mumd/src/notifications.rs @@ -1,14 +1,18 @@ +use log::*; + pub fn init() { #[cfg(feature = "notifications")] - libnotify::init("mumd").unwrap(); + if let Err(e) = libnotify::init("mumd") { + warn!("Unable to initialize notifications: {}", e); + } } #[cfg(feature = "notifications")] pub fn send(msg: String) -> Option<bool> { match libnotify::Notification::new("mumd", Some(msg.as_str()), None).show() { Ok(_) => Some(true), - Err(_) => { - log::debug!("Unable to send notification"); + Err(e) => { + warn!("Unable to send notification: {}", e); Some(false) } } diff --git a/mumd/src/state.rs b/mumd/src/state.rs index 20fe660..b52b330 100644 --- a/mumd/src/state.rs +++ b/mumd/src/state.rs @@ -3,9 +3,10 @@ pub mod server; pub mod user; use crate::audio::{Audio, NotificationEvents}; +use crate::error::StateError; use crate::network::{ConnectionInfo, VoiceStreamType}; use crate::network::tcp::{TcpEvent, TcpEventData}; -use crate::notify; +use crate::notifications; use crate::state::server::Server; use log::*; @@ -62,14 +63,14 @@ pub struct State { } impl State { - pub fn new() -> Self { - let config = mumlib::config::read_default_cfg(); + pub fn new() -> Result<Self, StateError> { + let config = mumlib::config::read_default_cfg()?; let phase_watcher = watch::channel(StatePhase::Disconnected); let audio = Audio::new( config.audio.input_volume.unwrap_or(1.0), config.audio.output_volume.unwrap_or(1.0), phase_watcher.1.clone(), - ); + ).map_err(|e| StateError::AudioError(e))?; let mut state = Self { config, server: None, @@ -77,7 +78,7 @@ impl State { phase_watcher, }; state.reload_config(); - state + Ok(state) } pub fn handle_command( @@ -462,7 +463,7 @@ impl State { == self.get_users_channel(self.server().unwrap().session_id().unwrap()) { if let Some(channel) = self.server().unwrap().channels().get(&channel_id) { - notify::send(format!( + notifications::send(format!( "{} connected and joined {}", &msg.get_name(), channel.name() @@ -515,7 +516,7 @@ impl State { self.get_users_channel(self.server().unwrap().session_id().unwrap()); if from_channel == this_channel || to_channel == this_channel { if let Some(channel) = self.server().unwrap().channels().get(&to_channel) { - notify::send(format!( + notifications::send(format!( "{} moved to channel {}", user.name(), channel.name() @@ -544,7 +545,7 @@ impl State { s += if deaf { " deafened" } else { " undeafened" }; } s += " themselves"; - notify::send(s); + notifications::send(s); } } } @@ -560,7 +561,7 @@ impl State { if this_channel == other_channel { self.audio.play_effect(NotificationEvents::UserDisconnected); if let Some(user) = self.server().unwrap().users().get(&msg.get_session()) { - notify::send(format!("{} disconnected", &user.name())); + notifications::send(format!("{} disconnected", &user.name())); } } @@ -573,7 +574,12 @@ impl State { } pub fn reload_config(&mut self) { - self.config = mumlib::config::read_default_cfg(); + match mumlib::config::read_default_cfg() { + Ok(config) => { + self.config = config; + } + Err(e) => error!("Couldn't read config: {}", e), + } if let Some(input_volume) = self.config.audio.input_volume { self.audio.set_input_volume(input_volume); } |
