mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
Read/write scenarios in JSON, so people can manipulate them in any language. #313
This commit is contained in:
parent
38ba8d55f3
commit
65e07d9cc7
@ -156,6 +156,14 @@ pub fn read_binary<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_object<T: DeserializeOwned>(path: String, timer: &mut Timer) -> Result<T, Error> {
|
||||
if path.ends_with(".bin") {
|
||||
maybe_read_binary(path, timer)
|
||||
} else {
|
||||
maybe_read_json(path, timer)
|
||||
}
|
||||
}
|
||||
|
||||
// For BTreeMaps with struct keys. See https://github.com/serde-rs/json/issues/402.
|
||||
|
||||
pub fn serialize_btreemap<S: Serializer, K: Serialize, V: Serialize>(
|
||||
@ -438,13 +446,13 @@ pub fn basename(path: &str) -> String {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn file_exists(path: String) -> bool {
|
||||
Path::new(&path).exists()
|
||||
pub fn file_exists<I: Into<String>>(path: I) -> bool {
|
||||
Path::new(&path.into()).exists()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn file_exists(path: String) -> bool {
|
||||
pub fn file_exists<I: Into<String>>(path: I) -> bool {
|
||||
SYSTEM_DATA
|
||||
.get_file(path.trim_start_matches("../data/system/"))
|
||||
.get_file(path.into().trim_start_matches("../data/system/"))
|
||||
.is_some()
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ pub use crate::collections::{
|
||||
pub use crate::io::{
|
||||
basename, deserialize_btreemap, deserialize_multimap, deserialize_usize, file_exists,
|
||||
find_next_file, find_prev_file, from_json, list_all_objects, list_dir, load_all_objects,
|
||||
maybe_read_binary, maybe_read_json, read_binary, read_json, serialize_btreemap,
|
||||
maybe_read_binary, maybe_read_json, read_binary, read_json, read_object, serialize_btreemap,
|
||||
serialize_multimap, serialize_usize, serialized_size_bytes, slurp_file, to_json, write_binary,
|
||||
write_json, FileWithProgress,
|
||||
};
|
||||
@ -119,9 +119,17 @@ pub fn path_prebaked_results(map_name: &str, scenario_name: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn path_scenario(map_name: &str, scenario_name: &str) -> String {
|
||||
path(format!(
|
||||
// TODO Getting complicated. Looks for .bin, then .json.
|
||||
let p = path(format!(
|
||||
"system/scenarios/{}/{}.bin",
|
||||
map_name, scenario_name
|
||||
));
|
||||
if file_exists(&p) {
|
||||
return p;
|
||||
}
|
||||
path(format!(
|
||||
"system/scenarios/{}/{}.json",
|
||||
map_name, scenario_name
|
||||
))
|
||||
}
|
||||
pub fn path_all_scenarios(map_name: &str) -> String {
|
||||
|
@ -652,6 +652,7 @@ impl<'a> Read for Timer<'a> {
|
||||
prettyprint_usize(file.total_bytes / 1024 / 1024),
|
||||
prettyprint_time(elapsed)
|
||||
);
|
||||
if self.outermost_name != "throwaway" {
|
||||
if file.last_printed_at.is_none() {
|
||||
self.println(line.clone());
|
||||
} else {
|
||||
@ -661,11 +662,13 @@ impl<'a> Read for Timer<'a> {
|
||||
sink.reprintln(line.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.stack.pop();
|
||||
self.add_result(elapsed, line);
|
||||
} else if file.last_printed_at.is_none()
|
||||
|| elapsed_seconds(file.last_printed_at.unwrap()) >= PROGRESS_FREQUENCY_SECONDS
|
||||
{
|
||||
if self.outermost_name != "throwaway" {
|
||||
let line = format!(
|
||||
"Reading {}: {}/{} MB... {}",
|
||||
file.path,
|
||||
@ -685,6 +688,7 @@ impl<'a> Read for Timer<'a> {
|
||||
sink.reprintln(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.last_printed_at = Some(Instant::now());
|
||||
}
|
||||
|
@ -154,7 +154,8 @@ problems is useful. It's also fine to crash when initially constructing all of
|
||||
the renderable map objects, because this crash will consistently happen at
|
||||
startup-time and be noticed by somebody developing before a player gets to it.
|
||||
|
||||
Regarding Testing: You'll surely note the lack of unit tests. If it bothers you, let's talk about
|
||||
what tests should exist. In the meantime, note lots of validation does happen
|
||||
via importing maps, running the prebaked scenarios, and screenshot diffing.
|
||||
Please read more in depth in the [testing strategy](https://dabreegster.github.io/abstreet/dev/testing.html) doc.
|
||||
Regarding Testing: You'll surely note the lack of unit tests. If it bothers you,
|
||||
let's talk about what tests should exist. In the meantime, note lots of
|
||||
validation does happen via importing maps, running the prebaked scenarios, and
|
||||
screenshot diffing. Please read more in depth in the
|
||||
[testing strategy](https://dabreegster.github.io/abstreet/dev/testing.html) doc.
|
||||
|
@ -51,18 +51,38 @@ are missing, etc. A summary of the commands available so far:
|
||||
normally. You can also later run the `headless` server with
|
||||
`--edits=name_of_edits`.
|
||||
|
||||
## Related tools
|
||||
|
||||
There's no API to create trips. Instead, you can
|
||||
[import trips from your own data](https://dabreegster.github.io/abstreet/trafficsim/travel_demand.html#custom-import).
|
||||
## Working with the map model
|
||||
|
||||
If you need to deeply inspect the map, you can dump it to JSON:
|
||||
|
||||
```
|
||||
cargo run --bin dump_map data/system/maps/montlake.bin
|
||||
cargo run --bin dump_map data/system/maps/montlake.bin > montlake.json
|
||||
```
|
||||
|
||||
The format of the map isn't well-documented yet. See the
|
||||
[generated API docs](https://dabreegster.github.io/abstreet/rustdoc/map_model/index.html)
|
||||
and [the map model docs](https://dabreegster.github.io/abstreet/map/index.html)
|
||||
in the meantime.
|
||||
|
||||
## Creating trips
|
||||
|
||||
There's no API yet to create trips. Instead, you can
|
||||
[import trips from your own data](https://dabreegster.github.io/abstreet/trafficsim/travel_demand.html#custom-import).
|
||||
|
||||
You can also dump Scenarios (the file that defines all of the people and trips)
|
||||
to JSON:
|
||||
|
||||
```
|
||||
cargo run --bin dump_scenario data/system/scenarios/montlake/weekday.bin > montlake_weekday.json
|
||||
```
|
||||
|
||||
You can modify the JSON, then put the file back in the appropriate directory and
|
||||
use it in-game:
|
||||
|
||||
```
|
||||
cargo run --bin game data/system/scenarios/montlake/modified_scenario.json
|
||||
```
|
||||
|
||||
The Scenario format is also undocumented, but see the
|
||||
[generated API docs](https://dabreegster.github.io/abstreet/rustdoc/sim/struct.Scenario.html)
|
||||
anyway.
|
||||
|
@ -117,7 +117,7 @@ impl GameplayMode {
|
||||
ScenarioGenerator::proletariat_robot(map, &mut rng, timer)
|
||||
} else {
|
||||
let path = abstutil::path_scenario(map.get_name(), &name);
|
||||
let mut scenario = match abstutil::maybe_read_binary(path.clone(), timer) {
|
||||
let mut scenario = match abstutil::read_object(path.clone(), timer) {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
Map::corrupt_err(path, err);
|
||||
|
9
importer/src/bin/dump_scenario.rs
Normal file
9
importer/src/bin/dump_scenario.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use abstutil::{CmdArgs, Timer};
|
||||
use sim::Scenario;
|
||||
|
||||
fn main() {
|
||||
let mut args = CmdArgs::new();
|
||||
let scenario: Scenario = abstutil::read_binary(args.required_free(), &mut Timer::throwaway());
|
||||
println!("{}", abstutil::to_json(&scenario));
|
||||
args.done();
|
||||
}
|
Loading…
Reference in New Issue
Block a user