From 10d55bd25c58e14ec9619ed7c9add02aab0d8de0 Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Sun, 14 Jun 2026 22:36:54 +0200 Subject: [PATCH] move netplay init (#1078) * move netplay init * more --- parallel-rdp/interface.cpp | 51 +++++++++++++++++++------------------- src/device.rs | 44 +++++++++++++++++++++++++++++--- src/device/cart/rom.rs | 13 ---------- src/lib.rs | 43 +++++++++++++++----------------- src/netplay.rs | 24 ++++++++++++++++-- src/ui/video.rs | 10 ++++---- 6 files changed, 112 insertions(+), 73 deletions(-) diff --git a/parallel-rdp/interface.cpp b/parallel-rdp/interface.cpp index 8290e68d..05c1e27f 100644 --- a/parallel-rdp/interface.cpp +++ b/parallel-rdp/interface.cpp @@ -622,10 +622,10 @@ static void render_frame(Vulkan::Device &device) { cmd->set_depth_test(false, false); cmd->set_cull_mode(VK_CULL_MODE_NONE); + VkViewport vp = cmd->get_viewport(); // If we don't have an image, we just get a cleared screen in the render // pass. if (image) { - VkViewport vp = cmd->get_viewport(); calculate_viewport(&vp.x, &vp.y, &vp.width, &vp.height, image->get_height() / gfx_info.upscale); @@ -646,33 +646,32 @@ static void render_frame(Vulkan::Device &device) { // The vertices are constants in the shader. // Draws fullscreen quad using oversized triangle. cmd->draw(3); - - if (!messages.empty()) { - Message *message = &messages.front(); - if (!message->image) { - message->image = create_message_image(device, vp.width, message_font, - message->message.c_str()); - SDL_AddTimer(message->milliseconds, pop_message_callback, NULL); - } - cmd->set_texture(0, 0, message->image->get_view(), - Vulkan::StockSampler::NearestClamp); - vp.x = floor(vp.x + (vp.width - message->image->get_width()) / 2); - vp.y = vp.y + vp.height - message->image->get_height(); - vp.height = message->image->get_height(); - vp.width = message->image->get_width(); - cmd->set_viewport(vp); - - cmd->draw(3); - } else if (achievement_progress_indicator_image) { - draw_indicator(cmd, achievement_progress_indicator_image, vp); - } else if (achievement_challenge_indicator_image && - display_challenge_indicator) { - draw_indicator(cmd, achievement_challenge_indicator_image, vp); + } + if (!messages.empty()) { + Message *message = &messages.front(); + if (!message->image) { + message->image = create_message_image(device, vp.width, message_font, + message->message.c_str()); + SDL_AddTimer(message->milliseconds, pop_message_callback, NULL); } + cmd->set_texture(0, 0, message->image->get_view(), + Vulkan::StockSampler::NearestClamp); + vp.x = floor(vp.x + (vp.width - message->image->get_width()) / 2); + vp.y = vp.y + vp.height - message->image->get_height(); + vp.height = message->image->get_height(); + vp.width = message->image->get_width(); + cmd->set_viewport(vp); - if (messages.empty() && display_fps && fps_image) { - draw_fps(cmd, vp); - } + cmd->draw(3); + } else if (achievement_progress_indicator_image) { + draw_indicator(cmd, achievement_progress_indicator_image, vp); + } else if (achievement_challenge_indicator_image && + display_challenge_indicator) { + draw_indicator(cmd, achievement_challenge_indicator_image, vp); + } + + if (messages.empty() && display_fps && fps_image) { + draw_fps(cmd, vp); } cmd->end_render_pass(); diff --git a/src/device.rs b/src/device.rs index c58b2e2d..4c03da3d 100644 --- a/src/device.rs +++ b/src/device.rs @@ -52,6 +52,7 @@ pub fn run_game( rom_contents: &[u8], game_settings: ui::GameSettings, ra_config: retroachievements::RAConfig, + netplay_config: Option, ) { device.cpu.overclock = game_settings.overclock; if game_settings.disable_expansion_pak { @@ -62,17 +63,35 @@ pub fn run_game( device.savestate.load_state = true; } - init_rng(device); - cart::rom::init(device, rom_contents); // cart needs to come before rdram // rdram pointer is shared with parallel-rdp and retroachievements rdram::init(device); - ui::video::init(device); + ui::video::init(device, netplay_config.is_some()); ui::audio::init(device); ui::input::init(&mut device.ui); + // must be after video init + if let Some(netplay_config) = &netplay_config { + device.netplay = netplay::init( + netplay_config.server_addr.clone(), + netplay_config.player_number, + netplay_config.number_of_players, + netplay_config.input_delay, + netplay_config.ice_config_path.clone(), + cart::rom::is_system_pal(rom_contents), + ); + if device.netplay.is_none() { + ui::input::close(&mut device.ui); + ui::audio::close(&mut device.ui); + ui::video::close(&device.ui); + return; + } + } + + init_rng_rtc(device); + // must be after video init let (discord_watch_tx, discord_handle) = if ra_config.enabled && device.netplay.is_none() { retroachievements::load_game(rom_contents, rom_contents.len(), ra_config.rich_presence) @@ -105,6 +124,11 @@ pub fn run_game( retroachievements::unload_game(discord_watch_tx, discord_handle); ui::storage::write_saves(device); + + if let Some(netplay) = &mut device.netplay { + netplay::close(netplay); + } + ui::input::close(&mut device.ui); ui::audio::close(&mut device.ui); ui::video::close(&device.ui); @@ -114,7 +138,7 @@ fn set_rng() -> rand::rngs::Xoshiro256PlusPlus { rand::rngs::Xoshiro256PlusPlus::try_from_rng(&mut rand::rngs::SysRng).unwrap() } -fn init_rng(device: &mut Device) { +fn init_rng_rtc(device: &mut Device) { let mut rng_seed = set_rng().next_u64(); if let Some(netplay) = &mut device.netplay { if netplay.player_number == 0 { @@ -124,6 +148,18 @@ fn init_rng(device: &mut Device) { } } device.rng = rand::rngs::Xoshiro256PlusPlus::seed_from_u64(rng_seed); + + device.cart.rtc_timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as i64; + if let Some(netplay) = &mut device.netplay { + if netplay.player_number == 0 { + netplay::send_rtc(netplay, device.cart.rtc_timestamp); + } else { + device.cart.rtc_timestamp = netplay::receive_rtc(netplay); + } + } } fn swap_rom(contents: Vec) -> Option> { diff --git a/src/device/cart/rom.rs b/src/device/cart/rom.rs index 369648ea..76f88666 100644 --- a/src/device/cart/rom.rs +++ b/src/device/cart/rom.rs @@ -1,5 +1,4 @@ use crate::device; -use crate::netplay; use crate::ui; use sha2::{Digest, Sha256}; @@ -122,18 +121,6 @@ pub fn dma_write( } pub fn init(device: &mut device::Device, rom_file: &[u8]) { - device.cart.rtc_timestamp = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs() as i64; - if let Some(netplay) = &mut device.netplay { - if netplay.player_number == 0 { - netplay::send_rtc(netplay, device.cart.rtc_timestamp); - } else { - device.cart.rtc_timestamp = netplay::receive_rtc(netplay); - } - } - device.cart.sc64.cfg[device::cart::sc64::SC64_BOOTLOADER_SWITCH as usize] = 1; device.cart.rom = rom_file.to_vec(); diff --git a/src/lib.rs b/src/lib.rs index bd49c79d..a3612624 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,25 +169,7 @@ pub fn run(args: Args, arg_count: usize) -> std::io::Result<()> { let mut shutdown_tx = None; let mut usb_handle = None; - let ice_config_path = dirs.cache_dir.join("ice_config.json"); - - if let Some(server_addr) = args.netplay_server_addr - && let Some(player_number) = args.netplay_player_number - && let Some(number_of_players) = args.netplay_number_of_players - && let Some(input_delay) = args.netplay_input_delay - { - device.netplay = netplay::init( - server_addr, - player_number, - number_of_players, - input_delay, - ice_config_path.clone(), - device::cart::rom::is_system_pal(&rom_contents), - ); - if device.netplay.is_none() { - return Err(Error::other("Failed to connect to netplay server")); - } - } else { + if args.netplay_server_addr.is_none() { for i in 0..4 { if device.ui.config.input.transfer_pak[i] && !device.ui.config.input.gb_rom_path[i].is_empty() @@ -213,7 +195,7 @@ pub fn run(args: Args, arg_count: usize) -> std::io::Result<()> { { let username = args.ra_username.unwrap_or(ra_config.username.clone()); retroachievements::init_client( - if cfg!(ra_hardcore_enabled) && device.netplay.is_none() { + if cfg!(ra_hardcore_enabled) && args.netplay_server_addr.is_none() { ra_config.hardcore } else { false @@ -240,6 +222,22 @@ pub fn run(args: Args, arg_count: usize) -> std::io::Result<()> { retroachievements::RAConfig::default() }; + let netplay_config = if let Some(server_addr) = args.netplay_server_addr + && let Some(player_number) = args.netplay_player_number + && let Some(number_of_players) = args.netplay_number_of_players + && let Some(input_delay) = args.netplay_input_delay + { + Some(netplay::NetplayConfig { + server_addr, + player_number, + number_of_players, + input_delay, + ice_config_path: dirs.cache_dir.join("ice_config.json"), + }) + } else { + None + }; + device::run_game( &mut device, &rom_contents, @@ -250,15 +248,14 @@ pub fn run(args: Args, arg_count: usize) -> std::io::Result<()> { load_savestate_slot: args.load_state, }, ra_config, + netplay_config, ); // on Android, the client is shut down in the app_window function #[cfg(not(target_os = "android"))] retroachievements::shutdown_client(); - if device.netplay.is_some() { - netplay::close(ice_config_path); - } else { + if device.netplay.is_none() { for i in 0..4 { if device.ui.config.input.transfer_pak[i] && !device.ui.config.input.gb_ram_path[i].is_empty() diff --git a/src/netplay.rs b/src/netplay.rs index 6bfa42d6..a0a45943 100644 --- a/src/netplay.rs +++ b/src/netplay.rs @@ -43,6 +43,14 @@ impl ggrs::NonBlockingSocket for MatchboxChannel { } } +pub struct NetplayConfig { + pub server_addr: String, + pub player_number: usize, + pub number_of_players: usize, + pub input_delay: usize, + pub ice_config_path: std::path::PathBuf, +} + pub struct Netplay { pub session: ggrs::P2PSession, pub reliable_channel: matchbox_socket::WebRtcChannel, @@ -56,6 +64,7 @@ pub struct Netplay { pub requests: std::collections::VecDeque>, pub incoming_message: Vec, pub disconnected_peers: tokio::sync::mpsc::Receiver, + pub ice_config_path: std::path::PathBuf, } #[derive(serde::Serialize, serde::Deserialize)] @@ -387,6 +396,12 @@ pub fn init( let timeout = std::time::Duration::from_secs(30); let mut player_numbers = std::collections::BTreeMap::new(); player_numbers.insert(player_number, None); + + ui::video::onscreen_message( + "Connecting to netplay peers...", + ui::video::MESSAGE_LENGTH_MESSAGE_SHORT, + ); + loop { socket.update_peers(); let peers = socket @@ -403,6 +418,9 @@ pub fn init( eprintln!("Could not connect to netplay peers"); return None; } + unsafe { sdl3_sys::events::SDL_PumpEvents() }; + ui::video::render_frame(); + ui::video::update_screen(); std::thread::sleep(std::time::Duration::from_millis(10)); } @@ -449,6 +467,7 @@ pub fn init( eprintln!("Could not start netplay session"); return None; } + unsafe { sdl3_sys::events::SDL_PumpEvents() }; std::thread::sleep(std::time::Duration::from_millis(10)); } @@ -470,9 +489,10 @@ pub fn init( requests: std::collections::VecDeque::new(), received_data: std::collections::VecDeque::new(), messages: std::collections::HashMap::new(), + ice_config_path, }) } -pub fn close(ice_config_path: std::path::PathBuf) { - let _ = std::fs::remove_file(ice_config_path); +pub fn close(netplay: &Netplay) { + let _ = std::fs::remove_file(&netplay.ice_config_path); } diff --git a/src/ui/video.rs b/src/ui/video.rs index 74daf0d3..e397637f 100644 --- a/src/ui/video.rs +++ b/src/ui/video.rs @@ -10,7 +10,7 @@ const NTSC_WIDESCREEN_WIDTH: i32 = 426; const NTSC_STANDARD_WIDTH: i32 = 320; const NTSC_HEIGHT: i32 = 240; -fn build_gfx_info(device: &mut device::Device) -> GFX_INFO { +fn build_gfx_info(device: &mut device::Device, netplay: bool) -> GFX_INFO { GFX_INFO { RDRAM: device.rdram.mem.as_mut_ptr(), DMEM: device.rsp.mem.as_mut_ptr(), @@ -22,7 +22,7 @@ fn build_gfx_info(device: &mut device::Device) -> GFX_INFO { PAL: device.cart.pal, widescreen: device.ui.config.video.widescreen, fullscreen: device.ui.video.fullscreen, - vsync: if device.netplay.is_none() { + vsync: if !netplay { device.ui.config.video.vsync } else { false @@ -34,7 +34,7 @@ fn build_gfx_info(device: &mut device::Device) -> GFX_INFO { } } -pub fn init(device: &mut device::Device) { +pub fn init(device: &mut device::Device, netplay: bool) { ui::sdl_init(sdl3_sys::init::SDL_INIT_VIDEO); ui::ttf_init(); @@ -99,7 +99,7 @@ pub fn init(device: &mut device::Device) { sdl3_sys::everything::SDL_HideCursor(); } - let gfx_info = build_gfx_info(device); + let gfx_info = build_gfx_info(device, netplay); unsafe { let font_bytes = include_bytes!("../../data/ui/RobotoMono-Regular.ttf"); @@ -170,7 +170,7 @@ pub fn idle() { } pub fn load_state(device: &mut device::Device, rdp_state: *const u8) { - let gfx_info = build_gfx_info(device); + let gfx_info = build_gfx_info(device, device.netplay.is_some()); unsafe { rdp_load_state(gfx_info, rdp_state); for reg in 0..device::vi::VI_REGS_COUNT {