From 23345321d8bbd5d4123d162226a3ff72dbbca395 Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Wed, 11 Nov 2026 18:12:29 +0000 Subject: [PATCH] enable rollback (#1378) --- README.md | 4 +- data/translations/de/LC_MESSAGES/gopher64.po | 4 +- data/translations/es/LC_MESSAGES/gopher64.po | 6 +-- data/translations/fr/LC_MESSAGES/gopher64.po | 4 +- data/translations/gopher64.pot | 4 +- data/translations/ja/LC_MESSAGES/gopher64.po | 4 +- data/translations/nl/LC_MESSAGES/gopher64.po | 4 +- .../pt_BR/LC_MESSAGES/gopher64.po | 4 +- data/translations/ru/LC_MESSAGES/gopher64.po | 4 +- src/netplay.rs | 39 ++----------------- src/ui/gui/netplay_wait.slint | 4 +- src/ui/netplay.rs | 23 +++++++---- src/ui/video.rs | 8 ++-- 13 files changed, 43 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index dc83523d..0f585301 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # gopher64 Gopher64 is a cross-platform N64 emulator. Some notable features: -* Netplay +* Rollback netplay * Homebrew support * Upscaling * CRT shader @@ -42,7 +42,7 @@ Keys are mapped according to [these defaults](https://github.com/gopher64/gopher ## netplay -Gopher64 supports P2P netplay (online play with others). +Gopher64 supports P2P rollback netplay (online play with others). Please read the [guide](https://github.com/gopher64/gopher64/wiki/Netplay-Guide) before trying out netplay. diff --git a/data/translations/de/LC_MESSAGES/gopher64.po b/data/translations/de/LC_MESSAGES/gopher64.po index 0dbef4dc..950746a5 100644 --- a/data/translations/de/LC_MESSAGES/gopher64.po +++ b/data/translations/de/LC_MESSAGES/gopher64.po @@ -417,11 +417,11 @@ msgstr "Nachricht senden" #: src/ui/gui/netplay_wait.slint:104 msgctxt "NetplayWait" msgid "" -"Setting the input delay too low will cause the game to lag.\n" +"Setting the input delay too low will cause frequent rollbacks.\n" "You can lower the input delay in-game by pressing Alt+[ and raise it with " "Alt+]." msgstr "" -"Eine zu niedrige Eingabeverzögerung führt zu Ruckeln im Spiel.\n" +"Eine zu niedrige Eingabeverzögerung führt zu häufigen Rollbacks.\n" "Sie können die Eingabeverzögerung im Spiel mit Alt+[ verringern und mit " "Alt+] erhöhen." diff --git a/data/translations/es/LC_MESSAGES/gopher64.po b/data/translations/es/LC_MESSAGES/gopher64.po index f4124388..90ee0934 100644 --- a/data/translations/es/LC_MESSAGES/gopher64.po +++ b/data/translations/es/LC_MESSAGES/gopher64.po @@ -415,12 +415,12 @@ msgstr "Enviar mensaje" #: src/ui/gui/netplay_wait.slint:104 msgctxt "NetplayWait" msgid "" -"Setting the input delay too low will cause the game to lag.\n" +"Setting the input delay too low will cause frequent rollbacks.\n" "You can lower the input delay in-game by pressing Alt+[ and raise it with " "Alt+]." msgstr "" -"Configurar un retardo de entrada demasiado bajo hará que el juego se " -"ralentice.\n" +"Configurar un retardo de entrada demasiado bajo provocará rollbacks " +"frecuentes.\n" "Puedes reducir el retardo de entrada en el juego pulsando Alt+[ y aumentarlo " "con Alt+]." diff --git a/data/translations/fr/LC_MESSAGES/gopher64.po b/data/translations/fr/LC_MESSAGES/gopher64.po index e8431d7c..4b36f278 100644 --- a/data/translations/fr/LC_MESSAGES/gopher64.po +++ b/data/translations/fr/LC_MESSAGES/gopher64.po @@ -417,11 +417,11 @@ msgstr "Envoyer" #: src/ui/gui/netplay_wait.slint:104 msgctxt "NetplayWait" msgid "" -"Setting the input delay too low will cause the game to lag.\n" +"Setting the input delay too low will cause frequent rollbacks.\n" "You can lower the input delay in-game by pressing Alt+[ and raise it with " "Alt+]." msgstr "" -"Régler le délai d'entrée trop bas provoquera des ralentissements.\n" +"Régler le délai d'entrée trop bas provoquera des rollbacks fréquents.\n" "Vous pouvez diminuer le délai d'entrée en jeu avec Alt+[ et l'augmenter avec " "Alt+]." diff --git a/data/translations/gopher64.pot b/data/translations/gopher64.pot index e33b039c..a12c439f 100644 --- a/data/translations/gopher64.pot +++ b/data/translations/gopher64.pot @@ -401,8 +401,8 @@ msgstr "" #: src/ui/gui/netplay_wait.slint:104 msgctxt "NetplayWait" msgid "" -"Setting the input delay too low will cause the game to lag.\nYou can lower " -"the input delay in-game by pressing Alt+[ and raise it with Alt+]." +"Setting the input delay too low will cause frequent rollbacks.\nYou can " +"lower the input delay in-game by pressing Alt+[ and raise it with Alt+]." msgstr "" #: src/ui/gui/netplay_wait.slint:112 diff --git a/data/translations/ja/LC_MESSAGES/gopher64.po b/data/translations/ja/LC_MESSAGES/gopher64.po index 923de1ea..4988818c 100644 --- a/data/translations/ja/LC_MESSAGES/gopher64.po +++ b/data/translations/ja/LC_MESSAGES/gopher64.po @@ -414,11 +414,11 @@ msgstr "送信" #: src/ui/gui/netplay_wait.slint:104 msgctxt "NetplayWait" msgid "" -"Setting the input delay too low will cause the game to lag.\n" +"Setting the input delay too low will cause frequent rollbacks.\n" "You can lower the input delay in-game by pressing Alt+[ and raise it with " "Alt+]." msgstr "" -"入力遅延を低く設定しすぎるとゲームが遅延します。\n" +"入力遅延を低く設定しすぎると頻繁にロールバックが発生します。\n" "Alt+[ でゲーム内の入力遅延を下げ、Alt+] で上げることができます。" #: src/ui/gui/netplay_wait.slint:112 diff --git a/data/translations/nl/LC_MESSAGES/gopher64.po b/data/translations/nl/LC_MESSAGES/gopher64.po index 5327be6a..8276e5bf 100644 --- a/data/translations/nl/LC_MESSAGES/gopher64.po +++ b/data/translations/nl/LC_MESSAGES/gopher64.po @@ -416,11 +416,11 @@ msgstr "Bericht verzenden" #: src/ui/gui/netplay_wait.slint:104 msgctxt "NetplayWait" msgid "" -"Setting the input delay too low will cause the game to lag.\n" +"Setting the input delay too low will cause frequent rollbacks.\n" "You can lower the input delay in-game by pressing Alt+[ and raise it with " "Alt+]." msgstr "" -"Een te lage invoervertraging zorgt ervoor dat het spel gaat haperen.\n" +"Een te lage invoervertraging zorgt voor frequente rollbacks.\n" "Je kunt de invoervertraging in het spel verlagen met Alt+[ en verhogen met " "Alt+]." diff --git a/data/translations/pt_BR/LC_MESSAGES/gopher64.po b/data/translations/pt_BR/LC_MESSAGES/gopher64.po index 51a6fb76..58183505 100644 --- a/data/translations/pt_BR/LC_MESSAGES/gopher64.po +++ b/data/translations/pt_BR/LC_MESSAGES/gopher64.po @@ -417,11 +417,11 @@ msgstr "Enviar Mensagem" #: src/ui/gui/netplay_wait.slint:104 msgctxt "NetplayWait" msgid "" -"Setting the input delay too low will cause the game to lag.\n" +"Setting the input delay too low will cause frequent rollbacks.\n" "You can lower the input delay in-game by pressing Alt+[ and raise it with " "Alt+]." msgstr "" -"Definir o atraso de entrada muito baixo causará lag no jogo.\n" +"Definir o atraso de entrada muito baixo causará rollbacks frequentes.\n" "Você pode diminuir o atraso de entrada no jogo pressionando Alt+[ e aumentá-" "lo com Alt+]." diff --git a/data/translations/ru/LC_MESSAGES/gopher64.po b/data/translations/ru/LC_MESSAGES/gopher64.po index eece0d2a..4f8366bb 100644 --- a/data/translations/ru/LC_MESSAGES/gopher64.po +++ b/data/translations/ru/LC_MESSAGES/gopher64.po @@ -416,11 +416,11 @@ msgstr "Отправить сообщение" #: src/ui/gui/netplay_wait.slint:104 msgctxt "NetplayWait" msgid "" -"Setting the input delay too low will cause the game to lag.\n" +"Setting the input delay too low will cause frequent rollbacks.\n" "You can lower the input delay in-game by pressing Alt+[ and raise it with " "Alt+]." msgstr "" -"Слишком низкая задержка ввода вызовет лаги в игре.\n" +"Слишком низкая задержка ввода вызовет частые откаты.\n" "Вы можете уменьшить задержку ввода в игре, нажав Alt+[, и увеличить её с " "помощью Alt+]." diff --git a/src/netplay.rs b/src/netplay.rs index 99033053..a6bd2141 100644 --- a/src/netplay.rs +++ b/src/netplay.rs @@ -107,7 +107,6 @@ fn process_reliable_messages(netplay: &mut Netplay) { .messages .insert(decoded_message.name, decoded_message.data); netplay.incoming_message.clear(); - check_input_delay(netplay); check_disconnect(netplay); } } @@ -203,20 +202,11 @@ pub fn receive_save(netplay: &mut Netplay, save_type: &str, save_data: &mut Vec< *save_data = message; } -pub fn send_input_delay(netplay: &mut Netplay, input_delay: usize) { +pub fn change_input_delay(netplay: &mut Netplay, input_delay: usize) { if !(1..=MAX_PREDICTION_WINDOW).contains(&input_delay) { return; } - let message = NetplayMessage { - name: "input_delay".to_string(), - data: (input_delay as u64).to_be_bytes().to_vec(), - }; - send_message(netplay, message); - change_input_delay(netplay, input_delay); -} - -fn change_input_delay(netplay: &mut Netplay, input_delay: usize) { netplay.input_delay = input_delay; for handle in netplay.session.local_player_handles() { if let Err(e) = netplay.session.set_input_delay(handle, input_delay) { @@ -230,15 +220,6 @@ fn change_input_delay(netplay: &mut Netplay, input_delay: usize) { } } -fn check_input_delay(netplay: &mut Netplay) { - if let Some(data) = netplay.messages.remove("input_delay") { - let input_delay = u64::from_be_bytes(data.try_into().unwrap()) as usize; - if input_delay != netplay.input_delay { - change_input_delay(netplay, input_delay); - } - } -} - fn check_disconnect(netplay: &mut Netplay) { if !netplay.disconnected && netplay.messages.remove("disconnect").is_some() { netplay.disconnected = true; @@ -272,7 +253,7 @@ pub fn process_requests( if let Some(request) = device.netplay.as_mut().unwrap().requests.pop_front() { match request { ggrs::GgrsRequest::SaveGameState { cell, frame } => { - //savestates::create_savestate(device, true, Some(frame)); + savestates::create_savestate(device, true, Some(frame)); let mut hasher = sha2::Sha256::new(); for reg in device.cpu.cop0.regs.as_ref() { @@ -341,26 +322,13 @@ fn process_netplay(device: &mut device::Device) { fn advance_frame(device: &mut device::Device) { let netplay = device.netplay.as_mut().unwrap(); - let local_input = if netplay.session.current_frame() > netplay.session.max_prediction() as i32 { - ui::input::get(&mut device.ui, 0) - } else { - //workaround for disabled rollback - ui::input::InputData::default() - }; + let local_input = ui::input::get(&mut device.ui, 0); let local_handle = *netplay.session.local_player_handles().first().unwrap(); netplay .session .add_local_input(local_handle, local_input) .unwrap(); - // avoid rollback - while !netplay.disconnected - && netplay.session.current_frame() > netplay.session.confirmed_frame() - && netplay.session.confirmed_frame() != ggrs::NULL_FRAME - { - poll_clients(netplay); - } - if netplay.disconnected { netplay.requests.push_back(ggrs::GgrsRequest::AdvanceFrame { inputs: vec![ @@ -503,6 +471,7 @@ pub fn init( .with_num_players(netplay_config.number_of_players) .unwrap() .with_input_delay(netplay_config.input_delay) + .with_sparse_saving_mode(true) .with_fps(if pal { 50 } else { 60 }) .unwrap() .with_desync_detection_mode(ggrs::DesyncDetection::On { interval: 60 }) diff --git a/src/ui/gui/netplay_wait.slint b/src/ui/gui/netplay_wait.slint index bb7159e3..9cb17370 100644 --- a/src/ui/gui/netplay_wait.slint +++ b/src/ui/gui/netplay_wait.slint @@ -101,12 +101,12 @@ export component NetplayWait inherits Page { } Text { - text: NetplayData.player_name == NetplayWaitData.players[0] ? @tr("Setting the input delay too low will cause the game to lag.\nYou can lower the input delay in-game by pressing Alt+[ and raise it with Alt+].") : NetplayWaitData.motd; + text: @tr("Setting the input delay too low will cause frequent rollbacks.\nYou can lower the input delay in-game by pressing Alt+[ and raise it with Alt+]."); horizontal-alignment: center; wrap: word-wrap; } - if NetplayData.player_name == NetplayWaitData.players[0]: HorizontalBox { + HorizontalBox { alignment: center; Text { text: @tr("Local Input Delay:"); diff --git a/src/ui/netplay.rs b/src/ui/netplay.rs index b8f029f6..5e459003 100644 --- a/src/ui/netplay.rs +++ b/src/ui/netplay.rs @@ -799,14 +799,21 @@ fn setup_wait_window( .upgrade_in_event_loop(move |handle| { let player_name = handle.get_netplay_player_name(); let players = handle.get_netplay_players(); - let input_delay = response - .sessions - .iter() - .next() - .unwrap() - .1 - .input_delay - .unwrap(); + // Input delay is per-player: use our own choice, falling back + // to the host's if our recommendation hasn't been calculated yet. + let local_input_delay = handle.get_netplay_input_delay(); + let input_delay = if local_input_delay > 0 { + local_input_delay as usize + } else { + response + .sessions + .iter() + .next() + .unwrap() + .1 + .input_delay + .unwrap() + }; let player_number = players.iter().position(|x| x == player_name).unwrap(); run_rom( diff --git a/src/ui/video.rs b/src/ui/video.rs index 8b2745a0..fa091af2 100644 --- a/src/ui/video.rs +++ b/src/ui/video.rs @@ -271,13 +271,11 @@ pub fn check_callback(device: &mut device::Device) -> (bool, bool) { ui::audio::raise_audio_volume(&mut device.ui); } - if let Some(netplay) = &mut device.netplay - && netplay.player_number == 0 - { + if let Some(netplay) = &mut device.netplay { if callback.decrease_input_delay { - netplay::send_input_delay(netplay, netplay.input_delay - 1); + netplay::change_input_delay(netplay, netplay.input_delay.saturating_sub(1)); } else if callback.increase_input_delay { - netplay::send_input_delay(netplay, netplay.input_delay + 1); + netplay::change_input_delay(netplay, netplay.input_delay + 1); } } (speed_limiter_toggled, callback.paused)