Skip to content

Commit

Permalink
bit more determinism
Browse files Browse the repository at this point in the history
  • Loading branch information
Uriopass committed Aug 3, 2023
1 parent 409e8da commit cb0da1b
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 83 deletions.
51 changes: 29 additions & 22 deletions egregoria/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -52,25 +52,25 @@ pub fn init() {
register_resource_noserialize::<ParCommandBuffer<WagonEnt>>();
register_resource_noserialize::<ParCommandBuffer<FreightStationEnt>>();
register_resource_noserialize::<ParCommandBuffer<CompanyEnt>>();
register_resource_noinit::<Market>("market");
register_resource_noinit::<EcoStats>("ecostats");
register_resource_noinit::<EgregoriaOptions>("egregoriaoptions");
register_resource_noinit::<Market, Bincode>("market");
register_resource_noinit::<EcoStats, Bincode>("ecostats");
register_resource_noinit::<EgregoriaOptions, Bincode>("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, Bincode>("tick");
register_resource_default::<Map, Bincode>("map");
register_resource_default::<TrainReservations, Bincode>("train_reservations");
register_resource_default::<Government, Bincode>("government");
register_resource_default::<ParkingManagement, Bincode>("pmanagement");
register_resource_default::<BuildingInfos, Bincode>("binfos");
register_resource::<GameTime, Bincode>("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::<CollisionWorld, Bincode>("coworld", || CollisionWorld::new(100));
register_resource::<RandProvider, Bincode>("randprovider", || RandProvider::new(RNG_SEED));
register_resource_default::<Dispatcher, Bincode>("dispatcher");
register_resource_default::<Replay, Bincode>("replay");
}

pub struct InitFunc {
Expand Down Expand Up @@ -128,29 +128,36 @@ fn register_resource_noserialize<T: 'static + Default + Send + Sync>() {
}
}

fn register_resource<T: 'static + Send + Sync + Serialize + DeserializeOwned>(
fn register_resource_default<
T: 'static + Send + Sync + Serialize + DeserializeOwned + Default,
E: Encoder,
>(
name: &'static str,
) {
register_resource::<T, E>(name, T::default);
}

fn register_resource<T: 'static + Send + Sync + Serialize + DeserializeOwned, E: Encoder>(
name: &'static str,
initializer: impl Fn() -> T + 'static,
) {
unsafe {
INIT_FUNCS.push(InitFunc {
f: Box::new(move |uiw| uiw.insert(initializer())),
});
register_resource_noinit::<T>(name);
register_resource_noinit::<T, E>(name);
}
}

fn register_resource_noinit<T: 'static + Send + Sync + Serialize + DeserializeOwned>(
fn register_resource_noinit<T: 'static + Send + Sync + Serialize + DeserializeOwned, E: Encoder>(
name: &'static str,
) {
unsafe {
SAVELOAD_FUNCS.push(SaveLoadFunc {
name,
save: Box::new(move |uiworld| {
<common::saveload::Bincode as Encoder>::encode(&*uiworld.read::<T>()).unwrap()
}),
save: Box::new(move |uiworld| E::encode(&*uiworld.read::<T>()).unwrap()),
load: Box::new(move |uiworld, data| {
if let Ok(res) = <common::saveload::Bincode as Encoder>::decode::<T>(&data) {
if let Ok(res) = E::decode::<T>(&data) {
uiworld.insert(res);
}
}),
Expand Down
32 changes: 17 additions & 15 deletions egregoria/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
12 changes: 6 additions & 6 deletions egregoria/src/map/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RoadID, Road>;
pub type Lanes = DenseSlotMap<LaneID, Lane>;
pub type Intersections = DenseSlotMap<IntersectionID, Intersection>;
pub type Buildings = DenseSlotMap<BuildingID, Building>;
pub type Lots = DenseSlotMap<LotID, Lot>;
pub type Roads = HopSlotMap<RoadID, Road>;
pub type Lanes = HopSlotMap<LaneID, Lane>;
pub type Intersections = HopSlotMap<IntersectionID, Intersection>;
pub type Buildings = HopSlotMap<BuildingID, Building>;
pub type Lots = HopSlotMap<LotID, Lot>;

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct MapProject {
Expand Down
5 changes: 3 additions & 2 deletions egregoria/src/map/objects/lot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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::<Vec<_>>();
.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));
Expand Down
18 changes: 2 additions & 16 deletions egregoria/src/map/spatial_map.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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};
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),
Expand All @@ -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<LotID> {
if let ProjectKind::Lot(id) = self {
Expand Down
4 changes: 2 additions & 2 deletions egregoria/src/map_dynamic/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -338,7 +338,7 @@ impl Router {
parking: &mut ParkingManagement,
map: &Map,
loc: &Location,
cars: &SlotMap<VehicleID, VehicleEnt>,
cars: &HopSlotMap<VehicleID, VehicleEnt>,
) -> Result<Vec<RoutingStep>, RouterError> {
let mut steps = vec![];
if let Location::Building(cur_build) = loc {
Expand Down
22 changes: 17 additions & 5 deletions egregoria/src/tests/test_iso.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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()
Expand All @@ -66,9 +67,10 @@ fn test_world_survives_serde() {
}
}
idx = next_idx;
idx = next_idx;*/

if goria.read::<Tick>().0 % 1000 != 0 {
let tick = goria.read::<Tick>().0;
if tick % 1000 != 0 || (tick < 7840) {
continue;
}

Expand All @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions egregoria/src/transportation/train.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -358,7 +358,7 @@ pub fn locomotive_desired_speed(
me: TrainID,
map: &Map,
reservs: &TrainReservations,
locos: &SlotMap<TrainID, TrainEnt>,
locos: &HopSlotMap<TrainID, TrainEnt>,
t: &TrainEnt,
) -> f32 {
if matches!(
Expand Down
26 changes: 13 additions & 13 deletions egregoria/src/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -185,24 +185,24 @@ impl GoriaDrop for CompanyEnt {

#[derive(Default, Serialize, Deserialize)]
pub struct World {
pub vehicles: SlotMap<VehicleID, VehicleEnt>,
pub humans: SlotMap<HumanID, HumanEnt>,
pub trains: SlotMap<TrainID, TrainEnt>,
pub wagons: SlotMap<WagonID, WagonEnt>,
pub freight_stations: SlotMap<FreightStationID, FreightStationEnt>,
pub companies: SlotMap<CompanyID, CompanyEnt>,
pub vehicles: HopSlotMap<VehicleID, VehicleEnt>,
pub humans: HopSlotMap<HumanID, HumanEnt>,
pub trains: HopSlotMap<TrainID, TrainEnt>,
pub wagons: HopSlotMap<WagonID, WagonEnt>,
pub freight_stations: HopSlotMap<FreightStationID, FreightStationEnt>,
pub companies: HopSlotMap<CompanyID, CompanyEnt>,
}

impl World {
pub fn get<E: EntityID>(&self, id: E) -> Option<&E::Entity> {
<<E as EntityID>::Entity as Entity>::storage(self).get(id)
}

pub fn storage<E: Entity>(&self) -> &SlotMap<E::ID, E> {
pub fn storage<E: Entity>(&self) -> &HopSlotMap<E::ID, E> {
E::storage(self)
}

pub fn storage_id<E: EntityID>(&self, _: E) -> &SlotMap<E, E::Entity> {
pub fn storage_id<E: EntityID>(&self, _: E) -> &HopSlotMap<E, E::Entity> {
E::Entity::storage(self)
}

Expand Down Expand Up @@ -311,8 +311,8 @@ impl World {
pub trait Entity: 'static + Sized + Send {
type ID: EntityID<Entity = Self>;

fn storage(w: &World) -> &SlotMap<Self::ID, Self>;
fn storage_mut(w: &mut World) -> &mut SlotMap<Self::ID, Self>;
fn storage(w: &World) -> &HopSlotMap<Self::ID, Self>;
fn storage_mut(w: &mut World) -> &mut HopSlotMap<Self::ID, Self>;
}

/// A trait that describes an entity id to be able to find an Entity from an ID
Expand Down Expand Up @@ -345,11 +345,11 @@ mod macros {
impl Entity for $obj {
type ID = $id;

fn storage(w: &World) -> &SlotMap<Self::ID, Self> {
fn storage(w: &World) -> &HopSlotMap<Self::ID, Self> {
&w.$s
}

fn storage_mut(w: &mut World) -> &mut SlotMap<Self::ID, Self> {
fn storage_mut(w: &mut World) -> &mut HopSlotMap<Self::ID, Self> {
&mut w.$s
}
}
Expand Down

0 comments on commit cb0da1b

Please sign in to comment.