mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 03:35:51 +03:00
get the game crate running in wasm. bundle in lots of data files, fake out a few more IO things, glue timer println's to console log
This commit is contained in:
parent
9a9619ec32
commit
d292e29ad0
@ -20,3 +20,7 @@ procfs = "0.4.7"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
termion = "1.5.1"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
include_dir = "0.5.0"
|
||||
stdweb = "0.4.20"
|
||||
|
@ -12,6 +12,9 @@ use std::fs::File;
|
||||
use std::io::{stdout, BufReader, BufWriter, Error, ErrorKind, Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
static SYSTEM_DATA: include_dir::Dir = include_dir::include_dir!("../data/system");
|
||||
|
||||
pub fn to_json<T: Serialize>(obj: &T) -> String {
|
||||
serde_json::to_string_pretty(obj).unwrap()
|
||||
}
|
||||
@ -36,6 +39,26 @@ pub fn write_json<T: Serialize>(path: String, obj: &T) {
|
||||
println!("Wrote {}", path);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn slurp_file(path: &str) -> Result<Vec<u8>, Error> {
|
||||
let mut file = File::open(path)?;
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn slurp_file(path: &str) -> Result<Vec<u8>, Error> {
|
||||
if let Some(raw) = SYSTEM_DATA.get_file(path.trim_start_matches("../data/system/")) {
|
||||
Ok(raw.contents().to_vec())
|
||||
} else {
|
||||
Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Can't slurp_file {}, it doesn't exist", path),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_read_json<T: DeserializeOwned>(path: String, timer: &mut Timer) -> Result<T, Error> {
|
||||
if !path.ends_with(".json") && !path.ends_with(".geojson") {
|
||||
panic!("read_json needs {} to end with .json or .geojson", path);
|
||||
@ -43,19 +66,11 @@ pub fn maybe_read_json<T: DeserializeOwned>(path: String, timer: &mut Timer) ->
|
||||
|
||||
timer.start(format!("parse {}", path));
|
||||
// TODO timer.read_file isn't working here. And we need to call stop() if there's no file.
|
||||
match File::open(&path) {
|
||||
Ok(mut file) => {
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
let obj: T = serde_json::from_str(&contents)?;
|
||||
timer.stop(format!("parse {}", path));
|
||||
Ok(obj)
|
||||
}
|
||||
Err(e) => {
|
||||
timer.stop(format!("parse {}", path));
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
let result: Result<T, Error> = slurp_file(&path).and_then(|raw| {
|
||||
serde_json::from_slice(&raw).map_err(|err| Error::new(ErrorKind::Other, err))
|
||||
});
|
||||
timer.stop(format!("parse {}", path));
|
||||
result
|
||||
}
|
||||
|
||||
pub fn read_json<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {
|
||||
@ -88,6 +103,7 @@ pub fn write_binary<T: Serialize>(path: String, obj: &T) {
|
||||
println!("Wrote {}", path);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn maybe_read_binary<T: DeserializeOwned>(path: String, timer: &mut Timer) -> Result<T, Error> {
|
||||
if !path.ends_with(".bin") {
|
||||
panic!("read_binary needs {} to end with .bin", path);
|
||||
@ -99,6 +115,23 @@ pub fn maybe_read_binary<T: DeserializeOwned>(path: String, timer: &mut Timer) -
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn maybe_read_binary<T: DeserializeOwned>(
|
||||
path: String,
|
||||
_timer: &mut Timer,
|
||||
) -> Result<T, Error> {
|
||||
if let Some(raw) = SYSTEM_DATA.get_file(path.trim_start_matches("../data/system/")) {
|
||||
let obj: T = bincode::deserialize(raw.contents())
|
||||
.map_err(|err| Error::new(ErrorKind::Other, err))?;
|
||||
Ok(obj)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Can't maybe_read_binary {}, it doesn't exist", path),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_binary<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {
|
||||
match maybe_read_binary(path.clone(), timer) {
|
||||
Ok(obj) => obj,
|
||||
@ -162,6 +195,7 @@ pub fn deserialize_multimap<
|
||||
}
|
||||
|
||||
// Just list all things from a directory, return sorted by name, with file extension removed.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn list_all_objects(dir: String) -> Vec<String> {
|
||||
let mut results: BTreeSet<String> = BTreeSet::new();
|
||||
match std::fs::read_dir(dir) {
|
||||
@ -187,8 +221,22 @@ pub fn list_all_objects(dir: String) -> Vec<String> {
|
||||
results.into_iter().collect()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn list_all_objects(dir: String) -> Vec<String> {
|
||||
let mut results = Vec::new();
|
||||
if let Some(dir) = SYSTEM_DATA.get_dir(dir.trim_start_matches("../data/system/")) {
|
||||
for f in dir.files() {
|
||||
results.push(format!("../data/system/{}", f.path().display()));
|
||||
}
|
||||
} else {
|
||||
panic!("Can't list_all_objects in {}", dir);
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
// Load all serialized things from a directory, return sorted by name, with file extension removed.
|
||||
// Detects JSON or binary.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn load_all_objects<T: DeserializeOwned>(dir: String) -> Vec<(String, T)> {
|
||||
let mut timer = Timer::new(format!("load_all_objects from {}", dir));
|
||||
let mut tree: BTreeMap<String, T> = BTreeMap::new();
|
||||
@ -224,6 +272,12 @@ pub fn load_all_objects<T: DeserializeOwned>(dir: String) -> Vec<(String, T)> {
|
||||
tree.into_iter().collect()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn load_all_objects<T: DeserializeOwned>(_dir: String) -> Vec<(String, T)> {
|
||||
// TODO
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
// TODO I'd like to get rid of this and just use Timer.read_file, but external libraries consume
|
||||
// the reader. :\
|
||||
pub struct FileWithProgress {
|
||||
|
@ -17,8 +17,8 @@ pub use crate::error::Error;
|
||||
pub use crate::io::{
|
||||
basename, deserialize_btreemap, deserialize_multimap, file_exists, find_next_file,
|
||||
find_prev_file, list_all_objects, load_all_objects, maybe_read_binary, maybe_read_json,
|
||||
read_binary, read_json, serialize_btreemap, serialize_multimap, serialized_size_bytes, to_json,
|
||||
write_binary, write_json, FileWithProgress,
|
||||
read_binary, read_json, serialize_btreemap, serialize_multimap, serialized_size_bytes,
|
||||
slurp_file, to_json, write_binary, write_json, FileWithProgress,
|
||||
};
|
||||
pub use crate::logs::Warn;
|
||||
pub use crate::random::{fork_rng, WeightedUsizeChoice};
|
||||
|
@ -151,7 +151,14 @@ impl<'a> Timer<'a> {
|
||||
|
||||
// Workaround for borrow checker
|
||||
fn selfless_println(maybe_sink: &mut Option<Box<dyn TimerSink + 'a>>, line: String) {
|
||||
println!("{}", line);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
println!("{}", line);
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
stdweb::console!(log, "%s", &line);
|
||||
}
|
||||
if let Some(ref mut sink) = maybe_sink {
|
||||
sink.println(line);
|
||||
}
|
||||
@ -311,8 +318,10 @@ impl<'a> Timer<'a> {
|
||||
O: Send,
|
||||
F: Send + Clone + Copy,
|
||||
{
|
||||
// Here's the sequential equivalent, to conveniently compare times
|
||||
if false {
|
||||
// Here's the sequential equivalent, to conveniently compare times. Also gotta use this in
|
||||
// wasm; no threads.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let mut results: Vec<O> = Vec::new();
|
||||
self.start_iter(timer_name, requests.len());
|
||||
for req in requests {
|
||||
@ -322,28 +331,31 @@ impl<'a> Timer<'a> {
|
||||
return results;
|
||||
}
|
||||
|
||||
scoped_threadpool::Pool::new(num_cpus::get() as u32).scoped(|scope| {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let mut results: Vec<Option<O>> = std::iter::repeat_with(|| None)
|
||||
.take(requests.len())
|
||||
.collect();
|
||||
for (idx, req) in requests.into_iter().enumerate() {
|
||||
let tx = tx.clone();
|
||||
scope.execute(move || {
|
||||
// TODO Can we catch panics here, dump a better stacktrace? ezgui runner does
|
||||
// this
|
||||
tx.send((idx, cb(req))).unwrap();
|
||||
});
|
||||
}
|
||||
drop(tx);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
scoped_threadpool::Pool::new(num_cpus::get() as u32).scoped(|scope| {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let mut results: Vec<Option<O>> = std::iter::repeat_with(|| None)
|
||||
.take(requests.len())
|
||||
.collect();
|
||||
for (idx, req) in requests.into_iter().enumerate() {
|
||||
let tx = tx.clone();
|
||||
scope.execute(move || {
|
||||
// TODO Can we catch panics here, dump a better stacktrace? ezgui runner
|
||||
// does this
|
||||
tx.send((idx, cb(req))).unwrap();
|
||||
});
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
self.start_iter(timer_name, results.len());
|
||||
for (idx, result) in rx.iter() {
|
||||
self.next();
|
||||
results[idx] = Some(result);
|
||||
}
|
||||
results.into_iter().map(|x| x.unwrap()).collect()
|
||||
})
|
||||
self.start_iter(timer_name, results.len());
|
||||
for (idx, result) in rx.iter() {
|
||||
self.next();
|
||||
results[idx] = Some(result);
|
||||
}
|
||||
results.into_iter().map(|x| x.unwrap()).collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Then the caller passes this in as a reader
|
||||
|
@ -6,3 +6,4 @@
|
||||
- std::process::exit
|
||||
- cargo magic
|
||||
- conditional compilation backend pattern, not traits with one impl at a time
|
||||
- scoped_threadpool
|
||||
|
@ -92,7 +92,9 @@ impl<'a> EventCtx<'a> {
|
||||
timer.start_iter("upload textures", num_textures);
|
||||
for (filename, tex_type) in textures {
|
||||
timer.next();
|
||||
let img = image::open(filename).unwrap().to_rgba();
|
||||
let img = image::load_from_memory(&abstutil::slurp_file(filename).unwrap())
|
||||
.unwrap()
|
||||
.to_rgba();
|
||||
let dims = img.dimensions();
|
||||
dims_to_textures.entry(dims).or_insert_with(Vec::new).push((
|
||||
filename.to_string(),
|
||||
|
@ -19,7 +19,8 @@ pub fn load_svg(prerender: &Prerender, filename: &str) -> (GeomBatch, Bounds) {
|
||||
return pair;
|
||||
}
|
||||
|
||||
let svg_tree = usvg::Tree::from_file(&filename, &usvg::Options::default()).unwrap();
|
||||
let raw = abstutil::slurp_file(&filename).unwrap();
|
||||
let svg_tree = usvg::Tree::from_data(&raw, &usvg::Options::default()).unwrap();
|
||||
let mut batch = GeomBatch::new();
|
||||
match add_svg_inner(&mut batch, svg_tree, HIGH_QUALITY) {
|
||||
Ok(bounds) => {
|
||||
|
@ -39,6 +39,13 @@ fn main() {
|
||||
opts.dev = true;
|
||||
flags.sim_flags.rng_seed = Some(42);
|
||||
}
|
||||
|
||||
// No random in wasm
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
flags.sim_flags.rng_seed = Some(42);
|
||||
}
|
||||
|
||||
if let Some(x) = args.optional("--color_scheme") {
|
||||
opts.color_scheme = Some(format!("../data/system/{}", x));
|
||||
}
|
||||
@ -81,5 +88,11 @@ fn main() {
|
||||
|
||||
args.done();
|
||||
|
||||
// TODO Montlake map isn't loading, just start here
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
flags.sim_flags.load = abstutil::path_synthetic_map("signal_single");
|
||||
}
|
||||
|
||||
ezgui::run(settings, |ctx| game::Game::new(flags, opts, mode, ctx));
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ impl SimFlags {
|
||||
(map, sim, rng)
|
||||
} else if self.load.starts_with(&abstutil::path_all_raw_maps())
|
||||
|| self.load.starts_with(&abstutil::path_all_synthetic_maps())
|
||||
|| self.load.starts_with(&abstutil::path_all_maps())
|
||||
{
|
||||
timer.note(format!("Loading map {}", self.load));
|
||||
|
||||
@ -105,16 +106,6 @@ impl SimFlags {
|
||||
let sim = Sim::new(&map, opts, timer);
|
||||
timer.stop("create sim");
|
||||
|
||||
(map, sim, rng)
|
||||
} else if self.load.starts_with(&abstutil::path_all_maps()) {
|
||||
timer.note(format!("Loading map {}", self.load));
|
||||
|
||||
let map = Map::new(self.load.clone(), false, timer);
|
||||
|
||||
timer.start("create sim");
|
||||
let sim = Sim::new(&map, opts, timer);
|
||||
timer.stop("create sim");
|
||||
|
||||
(map, sim, rng)
|
||||
} else {
|
||||
panic!("Don't know how to load {}", self.load);
|
||||
|
Loading…
Reference in New Issue
Block a user