move netplay init (#1078)

* move netplay init

* more
This commit is contained in:
Logan McNaughton
2026-06-14 22:36:54 +02:00
committed by GitHub
parent 16224cd5a6
commit 10d55bd25c
6 changed files with 112 additions and 73 deletions
+25 -26
View File
@@ -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();
+40 -4
View File
@@ -52,6 +52,7 @@ pub fn run_game(
rom_contents: &[u8],
game_settings: ui::GameSettings,
ra_config: retroachievements::RAConfig,
netplay_config: Option<netplay::NetplayConfig>,
) {
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<u8>) -> Option<Vec<u8>> {
-13
View File
@@ -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();
+20 -23
View File
@@ -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()
+22 -2
View File
@@ -43,6 +43,14 @@ impl ggrs::NonBlockingSocket<matchbox_socket::PeerId> 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<GgrsConfig>,
pub reliable_channel: matchbox_socket::WebRtcChannel,
@@ -56,6 +64,7 @@ pub struct Netplay {
pub requests: std::collections::VecDeque<ggrs::GgrsRequest<GgrsConfig>>,
pub incoming_message: Vec<u8>,
pub disconnected_peers: tokio::sync::mpsc::Receiver<matchbox_socket::PeerId>,
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);
}
+5 -5
View File
@@ -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 {