On the web, load different maps by making asynchronous HTTP calls. #21 (#364)

* On the web, load different maps by making asynchronous HTTP calls. #21

This is a very strange, specializd approach; it's unclear how it'll
generalize to reading scenarios and prebaked results. Ideally we could
call abstutil::read_binary as we currently do and somehow hide this async
trickery underneath, but I'm not sure how yet. In the meantime, this
moves us forward with the hack well-contained.

Next steps for web: stop bundling in all of data/system in the .wasm,
now that we can load from HTTP.
This commit is contained in:
Dustin Carlino 2020-10-08 10:51:13 -07:00 committed by GitHub
parent 69c8ce4cbe
commit 64bc4ee318
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 207 additions and 40 deletions

100
Cargo.lock generated
View File

@ -728,26 +728,51 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-channel"
version = "0.3.5"
name = "futures"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-executor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-io 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-task 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "futures-channel"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "futures-core"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-executor"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-task 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "futures-io"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-macro"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack 0.5.18 (registry+https://github.com/rust-lang/crates.io-index)",
@ -758,12 +783,12 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-task"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"once_cell 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -771,13 +796,15 @@ dependencies = [
[[package]]
name = "futures-util"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-io 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-macro 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-task 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-project 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -799,9 +826,12 @@ dependencies = [
"contour 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"downcast-rs 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"enumset 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"geojson 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"geom 0.1.0",
"instant 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"js-sys 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
"kml 0.1.0",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"lttb 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -817,6 +847,8 @@ dependencies = [
"svg_face 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-futures 0.4.18 (registry+https://github.com/rust-lang/crates.io-index)",
"web-sys 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
"webbrowser 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"widgetry 0.1.0",
"xmltree 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1023,9 +1055,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1119,9 +1151,9 @@ version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"h2 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1142,7 +1174,7 @@ version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.13.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"rustls 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2117,8 +2149,8 @@ dependencies = [
"base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding_rs 0.8.24 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-util 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.13.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2565,7 +2597,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2596,7 +2628,7 @@ name = "tokio-rustls"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustls 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2608,8 +2640,8 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-project-lite 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3265,13 +3297,15 @@ dependencies = [
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
"checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
"checksum futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
"checksum futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
"checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
"checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
"checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
"checksum futures 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5d8e3078b7b2a8a671cb7a3d17b4760e4181ea243227776ba83fd043b4ca034e"
"checksum futures-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74"
"checksum futures-core 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b"
"checksum futures-executor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "cc709ca1da6f66143b8c9bec8e6260181869893714e9b5a490b169b0414144ab"
"checksum futures-io 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5fc94b64bb39543b4e432f1790b6bf18e3ee3b74653c5449f63310e9a74b123c"
"checksum futures-macro 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f57ed14da4603b2554682e9f2ff3c65d7567b53188db96cb71538217fc64581b"
"checksum futures-sink 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd"
"checksum futures-task 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94"
"checksum futures-util 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34"
"checksum gdal 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ade14d6996db3d8cb123b4d6c5d10532a8ae94d9b23e834eed0c8c299d5e4756"
"checksum gdal-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f14384767373cfd3f2055aeac829d8cb146e80f73bb70ddaa000b9efa51f0979"
"checksum geo 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6315f3b85322e42fc9967e0f080fff8294f65eddfc222f1a1dd694955b04df4d"

View File

@ -14,6 +14,10 @@ pub fn from_json<T: DeserializeOwned>(raw: &Vec<u8>) -> Result<T, Error> {
serde_json::from_slice(raw).map_err(|err| Error::new(ErrorKind::Other, err))
}
pub fn from_binary<T: DeserializeOwned>(raw: &Vec<u8>) -> Result<T, Error> {
bincode::deserialize(raw).map_err(|err| Error::new(ErrorKind::Other, err))
}
pub fn serialized_size_bytes<T: Serialize>(obj: &T) -> usize {
bincode::serialized_size(obj).unwrap() as usize
}

View File

@ -9,7 +9,7 @@ crate-type = ["cdylib", "lib"]
[features]
default = ["built", "widgetry/glow-backend", "reqwest"]
wasm = ["console_log", "wasm-bindgen", "widgetry/wasm-backend"]
wasm = ["console_log", "futures", "futures-channel", "js-sys", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", "widgetry/wasm-backend"]
[dependencies]
aabb-quadtree = "0.1.0"
@ -21,9 +21,12 @@ console_log = { version = "0.1", optional = true }
contour = "0.2.0"
downcast-rs = "1.2.0"
enumset = "1.0.1"
futures = { version = "0.3.5", optional = true }
futures-channel = { version = "0.3.6", optional = true }
geojson = "0.19.0"
geom = { path = "../geom" }
instant = "0.1.7"
js-sys = { version = "0.3.45", optional = true }
kml = { path = "../kml" }
log = "0.4.11"
lttb = "0.2.0"
@ -38,6 +41,7 @@ serde_json = "1.0.57"
svg_face = "0.1.2"
sim = { path = "../sim" }
wasm-bindgen = { version = "0.2.68", optional = true }
wasm-bindgen-futures = { version = "0.4.18", optional = true }
webbrowser = "0.5.5"
widgetry = { path = "../widgetry", default-features=false }
xmltree = "0.10.1"
@ -45,3 +49,15 @@ xmltree = "0.10.1"
[build-dependencies]
built = "0.4.2"
walkdir = "2.2.7"
[dependencies.web-sys]
version = "0.3.4"
optional = true
features = [
"Headers",
"Request",
"RequestInit",
"RequestMode",
"Response",
"Window",
]

1
game/pkg/index.html Symbolic link
View File

@ -0,0 +1 @@
../index.html

1
game/pkg/system Symbolic link
View File

@ -0,0 +1 @@
../../data/system/

View File

@ -2,6 +2,5 @@
set -e
wasm-pack build --dev --target web -- --no-default-features --features wasm
cp index.html pkg
cd pkg
python3 -m http.server 8000

View File

@ -114,10 +114,9 @@ impl State for CityPicker {
return Transition::Pop;
}
name => {
return ctx.loading_screen("switch map", |ctx, _| {
app.switch_map(ctx, abstutil::path_map(name));
(self.on_load)(ctx, app)
});
let on_load =
std::mem::replace(&mut self.on_load, Box::new(|_, _| Transition::Keep));
return switch_map(ctx, app, name, on_load);
}
},
_ => {}
@ -185,3 +184,116 @@ impl State for CityPicker {
}
}
}
// Natively, we can blockingly load from the filesystem as usual.
#[cfg(not(target_arch = "wasm32"))]
fn switch_map(
ctx: &mut EventCtx,
app: &mut App,
name: &str,
on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>,
) -> Transition {
ctx.loading_screen("switch map", |ctx, _| {
app.switch_map(ctx, abstutil::path_map(name));
(on_load)(ctx, app)
})
}
// On the web, we asynchronously download the map file.
#[cfg(target_arch = "wasm32")]
fn switch_map(
_: &mut EventCtx,
_: &mut App,
name: &str,
on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>,
) -> Transition {
Transition::Replace(loader::AsyncFileLoader::new(
format!("http://0.0.0.0:8000/system/maps/{}.bin", name),
on_load,
))
}
#[cfg(target_arch = "wasm32")]
mod loader {
use futures_channel::oneshot;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response};
use abstutil::Timer;
use map_model::Map;
use widgetry::UpdateType;
use super::*;
use crate::render::DrawMap;
// Instead of blockingly reading a file within ctx.loading_screen, on the web have to
// asynchronously make an HTTP request and keep "polling" for completion in a way that's
// compatible with winit's event loop.
pub struct AsyncFileLoader {
response: oneshot::Receiver<Vec<u8>>,
on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>,
}
impl AsyncFileLoader {
pub fn new(
url: String,
on_load: Box<dyn Fn(&mut EventCtx, &mut App) -> Transition>,
) -> Box<dyn State> {
// Make the HTTP request nonblockingly. When the response is received, send it through
// the channel.
let (tx, rx) = oneshot::channel();
wasm_bindgen_futures::spawn_local(async move {
let mut opts = RequestInit::new();
opts.method("GET");
opts.mode(RequestMode::Cors);
let request = Request::new_with_str_and_init(&url, &opts).unwrap();
let window = web_sys::window().unwrap();
let resp_value = JsFuture::from(window.fetch_with_request(&request))
.await
.unwrap();
let resp: Response = resp_value.dyn_into().unwrap();
let buf = JsFuture::from(resp.array_buffer().unwrap()).await.unwrap();
let array = js_sys::Uint8Array::new(&buf);
let bytes = array.to_vec();
tx.send(bytes).unwrap();
});
Box::new(AsyncFileLoader {
response: rx,
on_load,
})
}
}
impl State for AsyncFileLoader {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
if let Some(resp) = self.response.try_recv().unwrap() {
let map: Map = abstutil::from_binary(&resp).unwrap();
// TODO This is a hack, repeating only some parts of app.switch_map. Refactor.
let bounds = map.get_bounds();
ctx.canvas.map_dims = (bounds.width(), bounds.height());
app.primary.map = map;
let mut timer = Timer::new("switch maps");
app.primary.draw_map =
DrawMap::new(&app.primary.map, &app.opts, &app.cs, ctx, &mut timer);
app.primary.clear_sim();
return (self.on_load)(ctx, app);
}
// Until the response is received, just ask winit to regularly call event(), so we can
// keep polling the channel.
ctx.request_update(UpdateType::Game);
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &App) {
// TODO Mimic the loading screen. Ideally even count the bytes received and have a
// progress bar.
g.clear(Color::RED);
}
}
}

View File

@ -0,0 +1 @@
../index.html

View File

@ -2,6 +2,5 @@
set -e
wasm-pack build --dev --target web -- --no-default-features --features wasm
cp index.html pkg
cd pkg
python3 -m http.server 8000