diff --git a/src/netplay.rs b/src/netplay.rs index b88b203b..60267daa 100644 --- a/src/netplay.rs +++ b/src/netplay.rs @@ -3,6 +3,13 @@ use crate::savestates; use crate::ui; use sha2::digest::Digest; +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct RtcIceServerConfig { + pub urls: Vec, + pub username: Option, + pub credential: Option, +} + pub struct GgrsConfig; impl ggrs::Config for GgrsConfig { type Input = ui::input::InputData; @@ -346,10 +353,24 @@ pub fn init( input_delay: usize, pal: bool, ) -> Option { - let (mut socket, loop_fut) = matchbox_socket::WebRtcSocketBuilder::new(server_addr) + let mut builder = matchbox_socket::WebRtcSocketBuilder::new(server_addr) .add_unreliable_channel() - .add_reliable_channel() - .build(); + .add_reliable_channel(); + + if let ice_config_path = ui::get_dirs().cache_dir.join("ice_config.json") + && let Ok(ice_config) = std::fs::read(&ice_config_path) + && let Ok(ice_config) = serde_json::from_slice::(&ice_config) + { + builder = builder.ice_server(matchbox_socket::RtcIceServerConfig { + urls: ice_config.urls, + username: ice_config.username, + credential: ice_config.credential, + }); + } else { + eprintln!("Using default ICE config"); + } + + let (mut socket, loop_fut) = builder.build(); tokio::spawn(async move { if let Err(e) = loop_fut.await { eprintln!("WebRTC loop failed: {}", e); diff --git a/src/ui/netplay.rs b/src/ui/netplay.rs index 4acaa226..01e1f324 100644 --- a/src/ui/netplay.rs +++ b/src/ui/netplay.rs @@ -1,4 +1,5 @@ use crate::device; +use crate::netplay::RtcIceServerConfig; use crate::ui; use crate::ui::gui::{AppWindow, open_uri, run_rom, save_settings}; use futures::{SinkExt, StreamExt}; @@ -35,6 +36,7 @@ struct NetplaySession { players: Vec, server_address: Option, input_delay: Option, + ice_server_config: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -274,6 +276,7 @@ fn create_session( players: vec![player_name], server_address: None, input_delay: None, + ice_server_config: None, }; let create_session = NetplayLobbyMessage { message_type: MessageType::RequestCreateSession, @@ -321,6 +324,7 @@ fn create_session( close_ping_rx, session.server_address.as_ref().unwrap().clone(), session_name.into(), + session.ice_server_config.clone(), session.game_name.as_ref().unwrap().into(), handle.get_netplay_rom_path(), ui::GameSettings { @@ -384,6 +388,7 @@ fn join_session( players: vec![player_name], server_address: None, input_delay: None, + ice_server_config: None, }; let join_session = NetplayLobbyMessage { message_type: MessageType::RequestJoinSession, @@ -396,12 +401,22 @@ fn join_session( fn update_ping( server_addr: String, + ice_server_config: Option, mut close_ping_rx: tokio::sync::broadcast::Receiver<()>, weak_app: slint::Weak, ) { - let (mut socket, loop_fut) = matchbox_socket::WebRtcSocketBuilder::new(server_addr) - .add_unreliable_channel() - .build(); + let mut builder = + matchbox_socket::WebRtcSocketBuilder::new(server_addr).add_unreliable_channel(); + if let Some(ice_server_config) = ice_server_config { + builder = builder.ice_server(matchbox_socket::RtcIceServerConfig { + urls: ice_server_config.urls, + username: ice_server_config.username, + credential: ice_server_config.credential, + }); + } else { + eprintln!("Using default ICE config"); + } + let (mut socket, loop_fut) = builder.build(); tokio::spawn(async move { if let Err(e) = loop_fut.await { @@ -525,6 +540,22 @@ fn update_ping( }); } +fn update_ice_config(ice_server_config: Option) { + tokio::spawn(async move { + let ice_config_path = ui::get_dirs().cache_dir.join("ice_config.json"); + if let Some(ice_server_config) = ice_server_config { + tokio::fs::write( + &ice_config_path, + serde_json::to_vec(&ice_server_config).unwrap(), + ) + .await + .unwrap(); + } else { + let _ = tokio::fs::remove_file(&ice_config_path).await; + } + }); +} + #[allow(clippy::too_many_arguments)] fn setup_wait_window( netplay_write_sender: tokio::sync::broadcast::Sender>, @@ -532,12 +563,19 @@ fn setup_wait_window( close_ping_rx: tokio::sync::broadcast::Receiver<()>, server_addr: String, session_name: slint::SharedString, + ice_server_config: Option, game_name: slint::SharedString, rom_path: slint::SharedString, game_settings: ui::GameSettings, app: &AppWindow, ) { - update_ping(server_addr.clone(), close_ping_rx, app.as_weak()); + update_ice_config(ice_server_config.clone()); + update_ping( + server_addr.clone(), + ice_server_config, + close_ping_rx, + app.as_weak(), + ); app.set_netplay_session_name(session_name); app.set_netplay_game_name(game_name); @@ -746,6 +784,7 @@ fn setup_join_window( close_ping_rx.resubscribe(), session.server_address.as_ref().unwrap().clone(), session_name.into(), + session.ice_server_config.clone(), session.game_name.as_ref().unwrap().into(), handle.get_netplay_rom_path(), ui::GameSettings {