From 5cf0b72baca7c94050ecbd4686dcdd3c157e35dc Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Sun, 19 Apr 2020 12:40:21 -0700 Subject: [PATCH] avoid a few crashes, mostly involving trips that were aborted in the baseline --- game/src/common/warp.rs | 19 ++++++++++++++++++- game/src/info/person.rs | 12 ++++++++++-- game/src/info/trip.rs | 9 +++++---- game/src/sandbox/dashboards/trip_table.rs | 7 ++++++- game/src/sandbox/gameplay/commute.rs | 1 + sim/src/analytics.rs | 10 +++++++--- sim/src/sim.rs | 7 +++++-- sim/src/trips.rs | 4 ++-- 8 files changed, 54 insertions(+), 15 deletions(-) diff --git a/game/src/common/warp.rs b/game/src/common/warp.rs index c2ff92ec41..c9bd440390 100644 --- a/game/src/common/warp.rs +++ b/game/src/common/warp.rs @@ -6,6 +6,7 @@ use crate::sandbox::SandboxMode; use ezgui::{EventCtx, GfxCtx, Warper, Wizard}; use geom::Pt2D; use map_model::{AreaID, BuildingID, IntersectionID, LaneID, RoadID}; +use maplit::btreemap; use sim::{PedestrianID, PersonID, TripID}; use std::collections::BTreeMap; @@ -110,6 +111,7 @@ fn inner_warp(ctx: &mut EventCtx, app: &mut App, line: &str) -> Option ID::Pedestrian(PedestrianID(idx)), 'P' => { let id = PersonID(idx); + app.primary.sim.lookup_person(id)?; return Some(Transition::PopWithData(Box::new(move |state, app, ctx| { // Other states pretty much don't use info panels. if let Some(ref mut s) = state.downcast_mut::() { @@ -128,7 +130,22 @@ fn inner_warp(ctx: &mut EventCtx, app: &mut App, line: &str) -> Option ID::from_agent(app.primary.sim.trip_to_agent(TripID(idx)).ok()?), + 't' => { + let trip = TripID(idx); + let person = app.primary.sim.trip_to_person(trip); + return Some(Transition::PopWithData(Box::new(move |state, app, ctx| { + // Other states pretty much don't use info panels. + if let Some(ref mut s) = state.downcast_mut::() { + let mut actions = s.contextual_actions(); + s.controls.common.as_mut().unwrap().launch_info_panel( + ctx, + app, + Tab::PersonTrips(person, btreemap! {trip => true}), + &mut actions, + ); + } + }))); + } 'T' => { let t = app.primary.map.lookup_turn_by_idx(idx)?; ID::Turn(t) diff --git a/game/src/info/person.rs b/game/src/info/person.rs index 587601f4f7..c39d268eb1 100644 --- a/game/src/info/person.rs +++ b/game/src/info/person.rs @@ -65,7 +65,11 @@ pub fn trips( // TODO No details. Weird case. assert!(wheres_waldo); wheres_waldo = false; - ("ongoing", Color::hex("#7FFA4D"), None) + ( + "ongoing", + Color::hex("#7FFA4D"), + open_trips.get(t).map(|_| Widget::nothing()), + ) } TripResult::TripDone => { assert!(wheres_waldo); @@ -79,7 +83,11 @@ pub fn trips( } TripResult::TripAborted => { // Aborted trips can happen anywhere in the schedule right now - ("cancelled", Color::hex("#EB3223"), None) + ( + "cancelled", + Color::hex("#EB3223"), + open_trips.get(t).map(|_| Widget::nothing()), + ) } TripResult::TripDoesntExist => unreachable!(), }; diff --git a/game/src/info/trip.rs b/game/src/info/trip.rs index 730b09eae6..b7a2c01ef3 100644 --- a/game/src/info/trip.rs +++ b/game/src/info/trip.rs @@ -111,15 +111,16 @@ pub fn future(ctx: &mut EventCtx, app: &App, trip: TripID, details: &mut Details let mut col = Vec::new(); - if app.has_prebaked().is_some() { - let phases = app.prebaked().get_trip_phases(trip, &app.primary.map); - let estimated_trip_time = - phases.last().as_ref().and_then(|p| p.end_time).unwrap() - start_time; + if let Some(estimated_trip_time) = app + .has_prebaked() + .and_then(|_| app.prebaked().finished_trip_time(trip)) + { col.extend(make_table( ctx, vec![("Estimated trip time", estimated_trip_time.to_string())], )); + let phases = app.prebaked().get_trip_phases(trip, &app.primary.map); col.push(make_timeline(ctx, app, trip, details, phases, None)); } else { // TODO Warp buttons. make_table is showing its age. diff --git a/game/src/sandbox/dashboards/trip_table.rs b/game/src/sandbox/dashboards/trip_table.rs index 19c48e3b29..f367ab4fb0 100644 --- a/game/src/sandbox/dashboards/trip_table.rs +++ b/game/src/sandbox/dashboards/trip_table.rs @@ -129,7 +129,12 @@ fn make(ctx: &mut EventCtx, app: &App, sort: SortBy, descending: bool) -> Compos let (_, waiting) = sim.finished_trip_time(*id).unwrap(); let (departure, _, _, _) = sim.trip_info(*id); let duration_before = if app.has_prebaked().is_some() { - app.prebaked().finished_trip_time(*id).unwrap() + if let Some(dt) = app.prebaked().finished_trip_time(*id) { + dt + } else { + // Aborted + continue; + } } else { Duration::ZERO }; diff --git a/game/src/sandbox/gameplay/commute.rs b/game/src/sandbox/gameplay/commute.rs index c06ce60b94..973053c9fb 100644 --- a/game/src/sandbox/gameplay/commute.rs +++ b/game/src/sandbox/gameplay/commute.rs @@ -178,6 +178,7 @@ fn get_score(app: &App, trips: &Vec) -> (Duration, Duration, usize) { if let Some((total, _)) = app.primary.sim.finished_trip_time(*t) { done += 1; after += total; + // Assume all trips completed before changes before += app.prebaked().finished_trip_time(*t).unwrap(); } } diff --git a/sim/src/analytics.rs b/sim/src/analytics.rs index 751a23425f..9bc2ac16a4 100644 --- a/sim/src/analytics.rs +++ b/sim/src/analytics.rs @@ -225,12 +225,16 @@ impl Analytics { (all, num_aborted, per_mode) } - // Ignores the current time. + // Ignores the current time. Returns None for aborted trips. pub fn finished_trip_time(&self, trip: TripID) -> Option { // TODO This is so inefficient! - for (_, id, _, dt) in &self.finished_trips { + for (_, id, maybe_mode, dt) in &self.finished_trips { if *id == trip { - return Some(*dt); + if maybe_mode.is_some() { + return Some(*dt); + } else { + return None; + } } } None diff --git a/sim/src/sim.rs b/sim/src/sim.rs index 821c5d836e..b938183f27 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -988,9 +988,12 @@ impl Sim { self.parking.get_car_owned_by(id) } - pub fn get_person(&self, id: PersonID) -> &Person { + pub fn lookup_person(&self, id: PersonID) -> Option<&Person> { self.trips.get_person(id) } + pub fn get_person(&self, id: PersonID) -> &Person { + self.trips.get_person(id).unwrap() + } pub fn get_all_people(&self) -> &Vec { self.trips.get_all_people() } @@ -1059,7 +1062,7 @@ impl Sim { TripResult::ModeChange } pub fn get_canonical_pt_per_person(&self, p: PersonID, map: &Map) -> Option { - match self.trips.get_person(p).state { + match self.trips.get_person(p)?.state { PersonState::Inside(b) => Some(map.get_b(b).polygon.center()), PersonState::Trip(t) => self.get_canonical_pt_per_trip(t, map).ok(), PersonState::OffMap | PersonState::Limbo => None, diff --git a/sim/src/trips.rs b/sim/src/trips.rs index 0563e2cf1a..2763fce171 100644 --- a/sim/src/trips.rs +++ b/sim/src/trips.rs @@ -699,8 +699,8 @@ impl TripManager { people } - pub fn get_person(&self, p: PersonID) -> &Person { - &self.people[p.0] + pub fn get_person(&self, p: PersonID) -> Option<&Person> { + self.people.get(p.0) } pub fn get_all_people(&self) -> &Vec { &self.people