From cb0da1b994f4b0d1a15b853e6b6b2a07c2c5e1e4 Mon Sep 17 00:00:00 2001 From: Paris DOUADY Date: Thu, 3 Aug 2023 13:27:39 +0200 Subject: [PATCH] bit more determinism --- egregoria/src/init.rs | 51 +++++++++++++++------------ egregoria/src/lib.rs | 32 +++++++++-------- egregoria/src/map/map.rs | 12 +++---- egregoria/src/map/objects/lot.rs | 5 +-- egregoria/src/map/spatial_map.rs | 18 ++-------- egregoria/src/map_dynamic/router.rs | 4 +-- egregoria/src/tests/test_iso.rs | 22 +++++++++--- egregoria/src/transportation/train.rs | 4 +-- egregoria/src/world.rs | 26 +++++++------- 9 files changed, 91 insertions(+), 83 deletions(-) diff --git a/egregoria/src/init.rs b/egregoria/src/init.rs index 8ff7a49c..281d8e23 100644 --- a/egregoria/src/init.rs +++ b/egregoria/src/init.rs @@ -22,7 +22,7 @@ use crate::{ ParCommandBuffer, RandProvider, Replay, RunnableSystem, RNG_SEED, SECONDS_PER_DAY, SECONDS_PER_HOUR, }; -use common::saveload::Encoder; +use common::saveload::{Bincode, Encoder}; use serde::de::DeserializeOwned; use serde::Serialize; @@ -52,25 +52,25 @@ pub fn init() { register_resource_noserialize::>(); register_resource_noserialize::>(); register_resource_noserialize::>(); - register_resource_noinit::("market"); - register_resource_noinit::("ecostats"); - register_resource_noinit::("egregoriaoptions"); + register_resource_noinit::("market"); + register_resource_noinit::("ecostats"); + register_resource_noinit::("egregoriaoptions"); register_init(init_market); - register_resource("tick", Tick::default); - register_resource("map", Map::default); - register_resource("train_reservations", TrainReservations::default); - register_resource("government", Government::default); - register_resource("pmanagement", ParkingManagement::default); - register_resource("binfos", BuildingInfos::default); - register_resource("game_time", || { + register_resource_default::("tick"); + register_resource_default::("map"); + register_resource_default::("train_reservations"); + register_resource_default::("government"); + register_resource_default::("pmanagement"); + register_resource_default::("binfos"); + register_resource::("game_time", || { GameTime::new(0.0, SECONDS_PER_DAY as f64 + 10.0 * SECONDS_PER_HOUR as f64) }); - register_resource("coworld", || CollisionWorld::new(100)); - register_resource("randprovider", || RandProvider::new(RNG_SEED)); - register_resource("dispatcher", Dispatcher::default); - register_resource("replay", Replay::default); + register_resource::("coworld", || CollisionWorld::new(100)); + register_resource::("randprovider", || RandProvider::new(RNG_SEED)); + register_resource_default::("dispatcher"); + register_resource_default::("replay"); } pub struct InitFunc { @@ -128,7 +128,16 @@ fn register_resource_noserialize() { } } -fn register_resource( +fn register_resource_default< + T: 'static + Send + Sync + Serialize + DeserializeOwned + Default, + E: Encoder, +>( + name: &'static str, +) { + register_resource::(name, T::default); +} + +fn register_resource( name: &'static str, initializer: impl Fn() -> T + 'static, ) { @@ -136,21 +145,19 @@ fn register_resource( INIT_FUNCS.push(InitFunc { f: Box::new(move |uiw| uiw.insert(initializer())), }); - register_resource_noinit::(name); + register_resource_noinit::(name); } } -fn register_resource_noinit( +fn register_resource_noinit( name: &'static str, ) { unsafe { SAVELOAD_FUNCS.push(SaveLoadFunc { name, - save: Box::new(move |uiworld| { - ::encode(&*uiworld.read::()).unwrap() - }), + save: Box::new(move |uiworld| E::encode(&*uiworld.read::()).unwrap()), load: Box::new(move |uiworld, data| { - if let Ok(res) = ::decode::(&data) { + if let Ok(res) = E::decode::(&data) { uiworld.insert(res); } }), diff --git a/egregoria/src/lib.rs b/egregoria/src/lib.rs index 7c35f53c..cb14e033 100644 --- a/egregoria/src/lib.rs +++ b/egregoria/src/lib.rs @@ -198,25 +198,27 @@ impl Egregoria { &mut self.world } - pub fn assert_equal(&self, other: &Self) { - assert_eq!( - self.resources.iter().count(), - other.resources.iter().count() - ); - - let serhashes = self.hashes(); - let deserhashes = other.hashes(); - - for (key, hash) in deserhashes.iter() { - assert_eq!(serhashes.get(key), Some(hash), "key: {:?}", key,); + pub fn is_equal(&self, other: &Self) -> bool { + if self.resources.iter().count() != other.resources.iter().count() { + return false; } - for (key, hash) in serhashes.iter() { - if deserhashes.get(key).is_some() { - continue; + unsafe { + for l in &SAVELOAD_FUNCS { + let a = (l.save)(self); + let b = (l.save)(other); + + if a != b { + std::fs::write(format!("{}_a.json", l.name), &*String::from_utf8_lossy(&a)) + .unwrap(); + std::fs::write(format!("{}_b.json", l.name), &*String::from_utf8_lossy(&b)) + .unwrap(); + return false; + } } - assert_eq!(deserhashes.get(key), Some(hash), "key: {:?}", key,); } + + true } #[profiling::function] diff --git a/egregoria/src/map/map.rs b/egregoria/src/map/map.rs index cd02c6f6..37ab2211 100644 --- a/egregoria/src/map/map.rs +++ b/egregoria/src/map/map.rs @@ -8,15 +8,15 @@ use geom::OBB; use geom::{Spline3, Vec2, Vec3}; use ordered_float::OrderedFloat; use serde::{Deserialize, Serialize}; -use slotmapd::DenseSlotMap; +use slotmapd::HopSlotMap; use std::collections::BTreeMap; use std::num::Wrapping; -pub type Roads = DenseSlotMap; -pub type Lanes = DenseSlotMap; -pub type Intersections = DenseSlotMap; -pub type Buildings = DenseSlotMap; -pub type Lots = DenseSlotMap; +pub type Roads = HopSlotMap; +pub type Lanes = HopSlotMap; +pub type Intersections = HopSlotMap; +pub type Buildings = HopSlotMap; +pub type Lots = HopSlotMap; #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct MapProject { diff --git a/egregoria/src/map/objects/lot.rs b/egregoria/src/map/objects/lot.rs index f2b8cc87..cb0e6efe 100644 --- a/egregoria/src/map/objects/lot.rs +++ b/egregoria/src/map/objects/lot.rs @@ -4,6 +4,7 @@ use geom::OBB; use geom::{Circle, Vec3}; use serde::{Deserialize, Serialize}; use slotmapd::new_key_type; +use std::collections::BTreeSet; new_key_type! { pub struct LotID; @@ -110,10 +111,10 @@ impl Lot { pub fn remove_intersecting_lots(map: &mut Map, road: RoadID) { let r = unwrap_retlog!(map.roads.get(road), "{:?} does not exist", road); - let mut to_remove = map + let mut to_remove: BTreeSet<_> = map .spatial_map .query(r.boldline(), ProjectFilter::LOT) - .collect::>(); + .collect(); let mut rp = |p: Circle| to_remove.extend(map.spatial_map.query(p, ProjectFilter::LOT)); rp(unwrap_ret!(map.intersections.get(r.src)).bcircle(&map.roads)); diff --git a/egregoria/src/map/spatial_map.rs b/egregoria/src/map/spatial_map.rs index c5e6b2da..677bdc4a 100644 --- a/egregoria/src/map/spatial_map.rs +++ b/egregoria/src/map/spatial_map.rs @@ -1,4 +1,5 @@ use crate::map::{BuildingID, IntersectionID, LotID, Map, RoadID}; +use derive_more::From; use flat_spatial::aabbgrid::AABBGridHandle; use flat_spatial::AABBGrid; use geom::{Circle, Intersect, Shape, ShapeEnum, Vec2, AABB}; @@ -6,7 +7,7 @@ use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::ops::{BitOr, Neg, Sub}; -#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, From)] pub enum ProjectKind { Inter(IntersectionID), Road(RoadID), @@ -15,21 +16,6 @@ pub enum ProjectKind { Ground, } -macro_rules! impl_from_pk { - ($t: ty, $e: expr) => { - impl From<$t> for ProjectKind { - fn from(x: $t) -> Self { - $e(x) - } - } - }; -} - -impl_from_pk!(IntersectionID, ProjectKind::Inter); -impl_from_pk!(RoadID, ProjectKind::Road); -impl_from_pk!(BuildingID, ProjectKind::Building); -impl_from_pk!(LotID, ProjectKind::Lot); - impl ProjectKind { pub fn to_lot(self) -> Option { if let ProjectKind::Lot(id) = self { diff --git a/egregoria/src/map_dynamic/router.rs b/egregoria/src/map_dynamic/router.rs index edfd3b1b..e0944e82 100644 --- a/egregoria/src/map_dynamic/router.rs +++ b/egregoria/src/map_dynamic/router.rs @@ -8,7 +8,7 @@ use crate::{ParCommandBuffer, World}; use egui_inspect::Inspect; use geom::{Spline3, Transform, Vec3}; use serde::{Deserialize, Serialize}; -use slotmapd::SlotMap; +use slotmapd::HopSlotMap; #[derive(Inspect, Serialize, Deserialize)] pub struct Router { @@ -338,7 +338,7 @@ impl Router { parking: &mut ParkingManagement, map: &Map, loc: &Location, - cars: &SlotMap, + cars: &HopSlotMap, ) -> Result, RouterError> { let mut steps = vec![]; if let Location::Building(cur_build) = loc { diff --git a/egregoria/src/tests/test_iso.rs b/egregoria/src/tests/test_iso.rs index c51a4bca..08e11da2 100644 --- a/egregoria/src/tests/test_iso.rs +++ b/egregoria/src/tests/test_iso.rs @@ -32,7 +32,7 @@ fn check_eq(w1: &World, w2: &World) -> bool { true } -#[test] // uncomment when slotmapd has been forked +//#[test] fn test_world_survives_serde() { init(); MyLog::init(); @@ -46,6 +46,7 @@ fn test_world_survives_serde() { while !loader.advance_tick(&mut goria, &mut s) { loader2.advance_tick(&mut goria2, &mut s); + /* let next_idx = idx + loader.replay.commands[idx..] .iter() @@ -66,9 +67,10 @@ fn test_world_survives_serde() { } } - idx = next_idx; + idx = next_idx;*/ - if goria.read::().0 % 1000 != 0 { + let tick = goria.read::().0; + if tick % 1000 != 0 || (tick < 7840) { continue; } @@ -82,8 +84,18 @@ fn test_world_survives_serde() { let ser = common::saveload::Bincode::encode(&goria).unwrap(); let mut deser: Egregoria = common::saveload::Bincode::decode(&ser).unwrap(); - deser.assert_equal(&goria); - deser.assert_equal(&goria2); + if !deser.is_equal(&goria) { + println!("not equal"); + deser.save_to_disk("world"); + goria.save_to_disk("world2"); + assert!(false); + } + if !deser.is_equal(&goria2) { + println!("not equal"); + deser.save_to_disk("world"); + goria2.save_to_disk("world2"); + assert!(false); + } std::mem::swap(&mut deser, &mut goria2); } diff --git a/egregoria/src/transportation/train.rs b/egregoria/src/transportation/train.rs index 0e4e0f5a..0f352424 100644 --- a/egregoria/src/transportation/train.rs +++ b/egregoria/src/transportation/train.rs @@ -7,7 +7,7 @@ use egui_inspect::Inspect; use geom::{PolyLine3, Polyline3Queue, Transform, Vec3}; use ordered_float::OrderedFloat; use serde::{Deserialize, Serialize}; -use slotmapd::SlotMap; +use slotmapd::HopSlotMap; use std::collections::btree_map::Entry; use std::collections::BTreeMap; @@ -358,7 +358,7 @@ pub fn locomotive_desired_speed( me: TrainID, map: &Map, reservs: &TrainReservations, - locos: &SlotMap, + locos: &HopSlotMap, t: &TrainEnt, ) -> f32 { if matches!( diff --git a/egregoria/src/world.rs b/egregoria/src/world.rs index fe18bb53..7612da47 100644 --- a/egregoria/src/world.rs +++ b/egregoria/src/world.rs @@ -17,7 +17,7 @@ use derive_more::{From, TryInto}; use geom::{Transform, Vec2, Vec3}; use serde::Deserialize; use slotmapd::__impl::Serialize; -use slotmapd::{new_key_type, SlotMap}; +use slotmapd::{new_key_type, HopSlotMap}; new_key_type! { pub struct VehicleID; @@ -185,12 +185,12 @@ impl GoriaDrop for CompanyEnt { #[derive(Default, Serialize, Deserialize)] pub struct World { - pub vehicles: SlotMap, - pub humans: SlotMap, - pub trains: SlotMap, - pub wagons: SlotMap, - pub freight_stations: SlotMap, - pub companies: SlotMap, + pub vehicles: HopSlotMap, + pub humans: HopSlotMap, + pub trains: HopSlotMap, + pub wagons: HopSlotMap, + pub freight_stations: HopSlotMap, + pub companies: HopSlotMap, } impl World { @@ -198,11 +198,11 @@ impl World { <::Entity as Entity>::storage(self).get(id) } - pub fn storage(&self) -> &SlotMap { + pub fn storage(&self) -> &HopSlotMap { E::storage(self) } - pub fn storage_id(&self, _: E) -> &SlotMap { + pub fn storage_id(&self, _: E) -> &HopSlotMap { E::Entity::storage(self) } @@ -311,8 +311,8 @@ impl World { pub trait Entity: 'static + Sized + Send { type ID: EntityID; - fn storage(w: &World) -> &SlotMap; - fn storage_mut(w: &mut World) -> &mut SlotMap; + fn storage(w: &World) -> &HopSlotMap; + fn storage_mut(w: &mut World) -> &mut HopSlotMap; } /// A trait that describes an entity id to be able to find an Entity from an ID @@ -345,11 +345,11 @@ mod macros { impl Entity for $obj { type ID = $id; - fn storage(w: &World) -> &SlotMap { + fn storage(w: &World) -> &HopSlotMap { &w.$s } - fn storage_mut(w: &mut World) -> &mut SlotMap { + fn storage_mut(w: &mut World) -> &mut HopSlotMap { &mut w.$s } }