From f6deb4b355bb41ac56ecff936d29f766005bba9b Mon Sep 17 00:00:00 2001 From: Sofia Date: Tue, 4 Aug 2026 21:26:15 +0300 Subject: [PATCH] Put loading replays on another thread, timeout recv --- rust/src/replay.rs | 80 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/rust/src/replay.rs b/rust/src/replay.rs index 469f2ee..4a3a460 100644 --- a/rust/src/replay.rs +++ b/rust/src/replay.rs @@ -4,6 +4,7 @@ use std::{ io::Cursor, sync::mpsc::{Receiver, channel}, thread, + time::Duration, }; use flate2::{Compression, bufread::GzDecoder, write::GzEncoder}; @@ -24,11 +25,17 @@ use crate::{ DamageSource, IPlayer, NetTransform, ball::Ball, buffs::Buffs, remote_player::RemotePlayer, telegrenade::Telegrenade, weapon::WeaponType, }, + popup_queue::{Popup, PopupQueue}, ui::{hud::Hud, replay_hud::ReplayHud}, }; pub const STANDALONE_REPLAY_GLOBAL: &str = "StandaloneReplayGlobal"; +pub enum ReplayReadEvent { + Done(Replay), + Error(String), +} + #[derive(GodotClass)] #[class(base=Node, init)] pub struct StandaloneReplayManager { @@ -50,6 +57,8 @@ pub struct StandaloneReplayManager { replay_hud: Option>, since_last_update: f64, + recv: Option>, + base: Base, } @@ -65,6 +74,37 @@ impl INode for StandaloneReplayManager { hud.bind_mut() .update_times(playback.bind().current_time, self.end_time, true); } + + if let Some(recv) = &self.recv { + match recv.recv_timeout(Duration::from_millis(10)) { + Ok(event) => { + self.recv.take(); + match event { + ReplayReadEvent::Done(replay) => { + self.loading_replay = true; + let map_idx = replay.map_idx; + self.replay = Some(replay); + if let Some(map) = + GameManager::singleton().bind().maps.get(map_idx as usize) + { + if let Some(scene) = &map.bind().scene { + self.base().get_tree().change_scene_to_packed(scene); + } + } + } + ReplayReadEvent::Error(err) => { + godot_error!("Error reading replay: {}", err); + PopupQueue::singleton().bind_mut().queue(Popup { + title: "Error!".to_string(), + message: format!("{}", err), + ok: true, + }); + } + }; + } + Err(_) => {} + } + } } } @@ -78,26 +118,32 @@ impl StandaloneReplayManager { } pub fn open_replay(&mut self, path: &GString) { - if let Some(mut file) = FileAccess::open(path, ModeFlags::READ) { - let mut bytes = Vec::new(); - while !file.eof_reached() { - bytes.extend(file.get_buffer(1_000_000).to_vec()); - } + if self.recv.is_some() { + return; + } - let cursor = Cursor::new(&mut bytes); - let decoder = GzDecoder::new(cursor); + let path = path.to_string(); + let (send, recv) = channel(); + self.recv = Some(recv); - if let Ok(res) = ciborium::from_reader::(decoder) { - self.loading_replay = true; - let map_idx = res.map_idx; - self.replay = Some(res); - if let Some(map) = GameManager::singleton().bind().maps.get(map_idx as usize) { - if let Some(scene) = &map.bind().scene { - self.base().get_tree().change_scene_to_packed(scene); - } + PopupQueue::singleton().bind_mut().queue(Popup { + title: "Loading".to_string(), + message: "loading replay".to_string(), + ok: false, + }); + + thread::spawn(move || match fs::read(path) { + Ok(mut bytes) => { + let cursor = Cursor::new(&mut bytes); + let decoder = GzDecoder::new(cursor); + + match ciborium::from_reader::(decoder) { + Ok(replay) => send.send(ReplayReadEvent::Done(replay)), + Err(err) => send.send(ReplayReadEvent::Error(err.to_string())), } } - } + Err(err) => send.send(ReplayReadEvent::Error(err.to_string())), + }); } pub fn on_map_loaded(&mut self, map: Gd) { @@ -562,7 +608,7 @@ impl INode for ReplayRecorder { fn process(&mut self, _: f64) { if let Some(recv) = &self.recv { - if let Ok(data) = recv.recv() { + if let Ok(data) = recv.recv_timeout(Duration::from_millis(10)) { match data { ReplaySaveEvent::Error(err) => self.signals().on_saved().emit(&err), ReplaySaveEvent::Done => self.signals().on_saved().emit(&String::new()),