mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
+25
-26
@@ -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
@@ -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>> {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user