mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
Make the native download progress screen show how many files are left.
This commit is contained in:
parent
3b9a7cfa1f
commit
38f5da4f80
@ -9,7 +9,8 @@ use crate::sandbox::gameplay::GameplayMode;
|
||||
use crate::sandbox::SandboxMode;
|
||||
|
||||
pub fn import(ctx: &mut EventCtx) -> Transition {
|
||||
let (_, progress_rx) = futures_channel::mpsc::channel(1);
|
||||
let (_, outer_progress_rx) = futures_channel::mpsc::channel(1);
|
||||
let (_, inner_progress_rx) = futures_channel::mpsc::channel(1);
|
||||
Transition::Push(FutureLoader::<App, Option<String>>::new(
|
||||
ctx,
|
||||
Box::pin(async {
|
||||
@ -21,7 +22,8 @@ pub fn import(ctx: &mut EventCtx) -> Transition {
|
||||
Box::new(move |_: &App| result);
|
||||
Ok(wrap)
|
||||
}),
|
||||
progress_rx,
|
||||
outer_progress_rx,
|
||||
inner_progress_rx,
|
||||
"Waiting for a file to be chosen",
|
||||
Box::new(|ctx, app, maybe_path| {
|
||||
if let Ok(Some(path)) = maybe_path {
|
||||
|
@ -476,11 +476,13 @@ impl State<App> for SandboxLoader {
|
||||
continue;
|
||||
}
|
||||
gameplay::LoadScenario::Future(future) => {
|
||||
let (_, progress_rx) = futures_channel::mpsc::channel(1);
|
||||
let (_, outer_progress_rx) = futures_channel::mpsc::channel(1);
|
||||
let (_, inner_progress_rx) = futures_channel::mpsc::channel(1);
|
||||
return Transition::Push(FutureLoader::<App, Scenario>::new(
|
||||
ctx,
|
||||
Box::pin(future),
|
||||
progress_rx,
|
||||
outer_progress_rx,
|
||||
inner_progress_rx,
|
||||
"Loading Scenario",
|
||||
Box::new(|_, _, scenario| {
|
||||
// TODO show error/retry alert?
|
||||
|
@ -429,8 +429,11 @@ where
|
||||
panel: Panel,
|
||||
receiver: oneshot::Receiver<Result<Box<dyn Send + FnOnce(&A) -> T>>>,
|
||||
on_load: Option<Box<dyn FnOnce(&mut EventCtx, &mut A, Result<T>) -> Transition<A>>>,
|
||||
progress_receiver: Option<mpsc::Receiver<String>>,
|
||||
last_progress: String,
|
||||
// These're just two different types of progress updates that callers can provide
|
||||
outer_progress_receiver: Option<mpsc::Receiver<String>>,
|
||||
inner_progress_receiver: Option<mpsc::Receiver<String>>,
|
||||
last_outer_progress: String,
|
||||
last_inner_progress: String,
|
||||
|
||||
// If Runtime is dropped, any active tasks will be canceled, so we retain it here even
|
||||
// though we never access it. It might make more sense for Runtime to live on App if we're
|
||||
@ -449,7 +452,8 @@ where
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
future: Pin<Box<dyn Future<Output = Result<Box<dyn Send + FnOnce(&A) -> T>>>>>,
|
||||
progress_receiver: mpsc::Receiver<String>,
|
||||
outer_progress_receiver: mpsc::Receiver<String>,
|
||||
inner_progress_receiver: mpsc::Receiver<String>,
|
||||
loading_title: &str,
|
||||
on_load: Box<dyn FnOnce(&mut EventCtx, &mut A, Result<T>) -> Transition<A>>,
|
||||
) -> Box<dyn State<A>> {
|
||||
@ -463,8 +467,10 @@ where
|
||||
panel: ctx.make_loading_screen(Text::from(loading_title)),
|
||||
receiver,
|
||||
on_load: Some(on_load),
|
||||
progress_receiver: Some(progress_receiver),
|
||||
last_progress: String::new(),
|
||||
outer_progress_receiver: Some(outer_progress_receiver),
|
||||
inner_progress_receiver: Some(inner_progress_receiver),
|
||||
last_outer_progress: String::new(),
|
||||
last_inner_progress: String::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -472,7 +478,8 @@ where
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
future: Pin<Box<dyn Send + Future<Output = Result<Box<dyn Send + FnOnce(&A) -> T>>>>>,
|
||||
progress_receiver: mpsc::Receiver<String>,
|
||||
outer_progress_receiver: mpsc::Receiver<String>,
|
||||
inner_progress_receiver: mpsc::Receiver<String>,
|
||||
loading_title: &str,
|
||||
on_load: Box<dyn FnOnce(&mut EventCtx, &mut A, Result<T>) -> Transition<A>>,
|
||||
) -> Box<dyn State<A>> {
|
||||
@ -489,8 +496,10 @@ where
|
||||
receiver,
|
||||
on_load: Some(on_load),
|
||||
runtime,
|
||||
progress_receiver: Some(progress_receiver),
|
||||
last_progress: String::new(),
|
||||
outer_progress_receiver: Some(outer_progress_receiver),
|
||||
inner_progress_receiver: Some(inner_progress_receiver),
|
||||
last_outer_progress: String::new(),
|
||||
last_inner_progress: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -508,15 +517,32 @@ where
|
||||
return on_load(ctx, app, Err(anyhow!("channel canceled")));
|
||||
}
|
||||
Ok(None) => {
|
||||
if let Some(ref mut rx) = self.progress_receiver {
|
||||
if let Some(ref mut rx) = self.outer_progress_receiver {
|
||||
// Read all of the progress that's happened
|
||||
loop {
|
||||
match rx.try_next() {
|
||||
Ok(Some(msg)) => {
|
||||
self.last_progress = msg;
|
||||
self.last_outer_progress = msg;
|
||||
}
|
||||
Ok(None) => {
|
||||
self.progress_receiver = None;
|
||||
self.outer_progress_receiver = None;
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
// No messages
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(ref mut rx) = self.inner_progress_receiver {
|
||||
loop {
|
||||
match rx.try_next() {
|
||||
Ok(Some(msg)) => {
|
||||
self.last_inner_progress = msg;
|
||||
}
|
||||
Ok(None) => {
|
||||
self.inner_progress_receiver = None;
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
@ -533,7 +559,8 @@ where
|
||||
"Time spent: {}",
|
||||
Duration::realtime_elapsed(self.started)
|
||||
)),
|
||||
Line(&self.last_progress),
|
||||
Line(&self.last_outer_progress),
|
||||
Line(&self.last_inner_progress),
|
||||
]));
|
||||
|
||||
// Until the response is received, just ask winit to regularly call event(), so we
|
||||
|
@ -40,10 +40,10 @@ fn prettyprint_bytes(bytes: u64) -> String {
|
||||
}
|
||||
let kb = (bytes as f64) / 1024.0;
|
||||
if kb < 1024.0 {
|
||||
return format!("{} kb", kb as usize);
|
||||
return format!("{} KB", kb as usize);
|
||||
}
|
||||
let mb = kb / 1024.0;
|
||||
format!("{} mb", mb as usize)
|
||||
format!("{} MB", mb as usize)
|
||||
}
|
||||
|
||||
/// Prompt to download a missing city. On either success or failure (maybe the player choosing to
|
||||
@ -70,16 +70,19 @@ pub fn prompt_to_download_missing_data<A: AppLike + 'static>(
|
||||
}
|
||||
|
||||
let cities = vec![map_name.to_data_pack_name()];
|
||||
let (progress_tx, progress_rx) = futures_channel::mpsc::channel(1000);
|
||||
let (outer_progress_tx, outer_progress_rx) = futures_channel::mpsc::channel(1000);
|
||||
let (inner_progress_tx, inner_progress_rx) = futures_channel::mpsc::channel(1000);
|
||||
Transition::Replace(FutureLoader::<A, Vec<String>>::new(
|
||||
ctx,
|
||||
Box::pin(async {
|
||||
let result = download_cities(cities, progress_tx).await;
|
||||
let result =
|
||||
download_cities(cities, outer_progress_tx, inner_progress_tx).await;
|
||||
let wrap: Box<dyn Send + FnOnce(&A) -> Vec<String>> =
|
||||
Box::new(move |_: &A| result);
|
||||
Ok(wrap)
|
||||
}),
|
||||
progress_rx,
|
||||
outer_progress_rx,
|
||||
inner_progress_rx,
|
||||
"Downloading missing files",
|
||||
Box::new(|ctx, _, maybe_messages| {
|
||||
let messages = match maybe_messages {
|
||||
@ -97,7 +100,11 @@ pub fn prompt_to_download_missing_data<A: AppLike + 'static>(
|
||||
))
|
||||
}
|
||||
|
||||
async fn download_cities(cities: Vec<String>, mut progress: mpsc::Sender<String>) -> Vec<String> {
|
||||
async fn download_cities(
|
||||
cities: Vec<String>,
|
||||
mut outer_progress: mpsc::Sender<String>,
|
||||
mut inner_progress: mpsc::Sender<String>,
|
||||
) -> Vec<String> {
|
||||
let mut data_packs = DataPacks {
|
||||
runtime: BTreeSet::new(),
|
||||
input: BTreeSet::new(),
|
||||
@ -117,24 +124,30 @@ async fn download_cities(cities: Vec<String>, mut progress: mpsc::Sender<String>
|
||||
|
||||
let num_files = manifest.entries.len();
|
||||
let mut messages = Vec::new();
|
||||
let mut files_so_far = 0;
|
||||
|
||||
for (path, entry) in manifest.entries {
|
||||
files_so_far += 1;
|
||||
let local_path = abstio::path(path.strip_prefix("data/").unwrap());
|
||||
let url = format!(
|
||||
"http://abstreet.s3-website.us-east-2.amazonaws.com/{}/{}.gz",
|
||||
version, path
|
||||
);
|
||||
// TODO How can we have two streams, and have this logging be the "outer" one? And show x /
|
||||
// y files or something.
|
||||
info!(
|
||||
"Downloading {} ({})",
|
||||
if let Err(err) = outer_progress.try_send(format!(
|
||||
"Downloading file {}/{}: {} ({})",
|
||||
files_so_far,
|
||||
num_files,
|
||||
url,
|
||||
prettyprint_bytes(entry.compressed_size_bytes)
|
||||
);
|
||||
)) {
|
||||
warn!("Couldn't send progress: {}", err);
|
||||
}
|
||||
|
||||
match abstio::download_bytes(&url, &mut progress)
|
||||
match abstio::download_bytes(&url, &mut inner_progress)
|
||||
.await
|
||||
.and_then(|bytes| {
|
||||
// TODO Instead of holding everything in memory like this, we could also try to
|
||||
// stream the gunzipping and output writing
|
||||
info!("Decompressing {}", path);
|
||||
std::fs::create_dir_all(std::path::Path::new(&local_path).parent().unwrap())
|
||||
.unwrap();
|
||||
|
Loading…
Reference in New Issue
Block a user