Put loading replays on another thread, timeout recv

This commit is contained in:
Sofia 2026-08-04 21:26:15 +03:00
parent 0cddfe93bb
commit f6deb4b355

View File

@ -4,6 +4,7 @@ use std::{
io::Cursor, io::Cursor,
sync::mpsc::{Receiver, channel}, sync::mpsc::{Receiver, channel},
thread, thread,
time::Duration,
}; };
use flate2::{Compression, bufread::GzDecoder, write::GzEncoder}; use flate2::{Compression, bufread::GzDecoder, write::GzEncoder};
@ -24,11 +25,17 @@ use crate::{
DamageSource, IPlayer, NetTransform, ball::Ball, buffs::Buffs, remote_player::RemotePlayer, DamageSource, IPlayer, NetTransform, ball::Ball, buffs::Buffs, remote_player::RemotePlayer,
telegrenade::Telegrenade, weapon::WeaponType, telegrenade::Telegrenade, weapon::WeaponType,
}, },
popup_queue::{Popup, PopupQueue},
ui::{hud::Hud, replay_hud::ReplayHud}, ui::{hud::Hud, replay_hud::ReplayHud},
}; };
pub const STANDALONE_REPLAY_GLOBAL: &str = "StandaloneReplayGlobal"; pub const STANDALONE_REPLAY_GLOBAL: &str = "StandaloneReplayGlobal";
pub enum ReplayReadEvent {
Done(Replay),
Error(String),
}
#[derive(GodotClass)] #[derive(GodotClass)]
#[class(base=Node, init)] #[class(base=Node, init)]
pub struct StandaloneReplayManager { pub struct StandaloneReplayManager {
@ -50,6 +57,8 @@ pub struct StandaloneReplayManager {
replay_hud: Option<Gd<ReplayHud>>, replay_hud: Option<Gd<ReplayHud>>,
since_last_update: f64, since_last_update: f64,
recv: Option<Receiver<ReplayReadEvent>>,
base: Base<Node>, base: Base<Node>,
} }
@ -65,6 +74,37 @@ impl INode for StandaloneReplayManager {
hud.bind_mut() hud.bind_mut()
.update_times(playback.bind().current_time, self.end_time, true); .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) { pub fn open_replay(&mut self, path: &GString) {
if let Some(mut file) = FileAccess::open(path, ModeFlags::READ) { if self.recv.is_some() {
let mut bytes = Vec::new(); return;
while !file.eof_reached() { }
bytes.extend(file.get_buffer(1_000_000).to_vec());
}
let cursor = Cursor::new(&mut bytes); let path = path.to_string();
let decoder = GzDecoder::new(cursor); let (send, recv) = channel();
self.recv = Some(recv);
if let Ok(res) = ciborium::from_reader::<Replay, _>(decoder) { PopupQueue::singleton().bind_mut().queue(Popup {
self.loading_replay = true; title: "Loading".to_string(),
let map_idx = res.map_idx; message: "loading replay".to_string(),
self.replay = Some(res); ok: false,
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); 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::<Replay, _>(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<Map>) { pub fn on_map_loaded(&mut self, map: Gd<Map>) {
@ -562,7 +608,7 @@ impl INode for ReplayRecorder {
fn process(&mut self, _: f64) { fn process(&mut self, _: f64) {
if let Some(recv) = &self.recv { 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 { match data {
ReplaySaveEvent::Error(err) => self.signals().on_saved().emit(&err), ReplaySaveEvent::Error(err) => self.signals().on_saved().emit(&err),
ReplaySaveEvent::Done => self.signals().on_saved().emit(&String::new()), ReplaySaveEvent::Done => self.signals().on_saved().emit(&String::new()),