Skip to content

Commit

Permalink
fix determinism around trees and test
Browse files Browse the repository at this point in the history
  • Loading branch information
Uriopass committed Jul 19, 2024
1 parent df4c776 commit 4ce3db1
Show file tree
Hide file tree
Showing 13 changed files with 1,065 additions and 12,783 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ jobs:
- name: Check
run: cargo check --verbose --workspace --tests --examples --features multiplayer
- name: Run tests
run: cargo test --verbose --workspace --features multiplayer
run: cargo test --verbose --workspace --features multiplayer --test-threads=1
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions prototypes/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![warn(clippy::iter_over_hash_type)]

use common::TransparentMap;
use geom::Vec2;
use mlua::{FromLua, Table};
Expand Down
2 changes: 2 additions & 0 deletions prototypes/src/validation.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::iter_over_hash_type)]

use thiserror::Error;

use common::error::MultiError;
Expand Down
2 changes: 2 additions & 0 deletions simulation/src/economy/market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,8 @@ fn calculate_prices(price_multiplier: f32) -> BTreeMap<ItemID, Money> {
* company.n_workers as f64
* WORKER_CONSUMPTION_PER_MINUTE;

dbg!(price_consumption, price_workers, qty);

let newprice = (price_consumption
+ Money::new_inner((price_workers.inner() as f32 * price_multiplier) as i64))
/ qty;
Expand Down
11 changes: 7 additions & 4 deletions simulation/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
use serde::de::DeserializeOwned;
use serde::Serialize;

#[allow(unused_imports)]
use common::saveload::{Bincode, Encoder, JSONPretty, JSON};
use prototypes::{GameTime, Tick};

use crate::economy::{market_update, EcoStats, Government, Market};
use crate::map::Map;
use crate::map_dynamic::{
Expand All @@ -22,10 +29,6 @@ use crate::{
add_souls_to_empty_buildings, utils, ParCommandBuffer, RandProvider, Replay, RunnableSystem,
Simulation, SimulationOptions, RNG_SEED,
};
use common::saveload::{Bincode, Encoder, JSON};
use prototypes::{GameTime, Tick};
use serde::de::DeserializeOwned;
use serde::Serialize;

pub fn init() {
//crate::rerun::init_rerun();
Expand Down
1 change: 1 addition & 0 deletions simulation/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(clippy::too_many_arguments)]
#![allow(clippy::type_complexity)]
#![warn(clippy::iter_over_hash_type)]

use crate::init::{GSYSTEMS, INIT_FUNCS, SAVELOAD_FUNCS};
use crate::map::{BuildingKind, Map};
Expand Down
83 changes: 43 additions & 40 deletions simulation/src/map/objects/intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,53 +110,56 @@ impl Intersection {
}

const MIN_INTERFACE: f32 = 9.0;
// allow slicing since we remove all roads not in self.roads
#[allow(clippy::indexing_slicing)]
pub fn update_interface_radius(&mut self, roads: &mut Roads) {
let id = self.id;

if let [] = *self.roads {
return;
}

if let [r1_id] = *self.roads {
let r = &mut roads[r1_id];
r.set_interface(id, Self::empty_interface(r.width));
return;
}

if let [r1_id, r2_id] = *self.roads {
let (r1, r2) = (&roads[r1_id], &roads[r2_id]);
let (dir1, dir2) = (r1.dir_from(id), r2.dir_from(id));
let (r1w, r2w) = (r1.width, r2.width);
let elbow = (dir1 + dir2) * 0.5;

if elbow.mag() < 0.001 {
roads[r1_id].set_interface(id, 1.0);
roads[r2_id].set_interface(id, 1.0);
match *self.roads {
[] => return,
[r1_id] => {
let r = &mut roads[r1_id];
r.set_interface(id, Self::empty_interface(r.width));
return;
}
[r1_id, r2_id] => {
let (r1, r2) = (&roads[r1_id], &roads[r2_id]);
let (dir1, dir2) = (r1.dir_from(id), r2.dir_from(id));
let (r1w, r2w) = (r1.width, r2.width);
let elbow = (dir1 + dir2) * 0.5;

if elbow.mag() < 0.001 {
roads[r1_id].set_interface(id, 1.0);
roads[r2_id].set_interface(id, 1.0);
return;
}

let ray1 = Ray::new(
self.pos.xy()
+ dir1.perpendicular() * dir1.perpendicular().dot(elbow).signum() * r1w * 0.5,
dir1,
);
let ray2 = Ray::new(
self.pos.xy()
+ dir2.perpendicular() * dir2.perpendicular().dot(elbow).signum() * r2w * 0.5,
dir2,
);

let Some((dist_a, dist_b)) = ray1.both_dist_to_inter(&ray2) else {
roads[r1_id].set_interface(id, Self::empty_interface(r1w));
roads[r2_id].set_interface(id, Self::empty_interface(r2w));
let ray1 = Ray::new(
self.pos.xy()
+ dir1.perpendicular()
* dir1.perpendicular().dot(elbow).signum()
* r1w
* 0.5,
dir1,
);
let ray2 = Ray::new(
self.pos.xy()
+ dir2.perpendicular()
* dir2.perpendicular().dot(elbow).signum()
* r2w
* 0.5,
dir2,
);

let Some((dist_a, dist_b)) = ray1.both_dist_to_inter(&ray2) else {
roads[r1_id].set_interface(id, Self::empty_interface(r1w));
roads[r2_id].set_interface(id, Self::empty_interface(r2w));
return;
};

roads[r1_id].set_interface(id, dist_a);
roads[r2_id].set_interface(id, dist_b);
return;
};

roads[r1_id].set_interface(id, dist_a);
roads[r2_id].set_interface(id, dist_b);
return;
}
_ => {}
}

for &r in &self.roads {
Expand Down
6 changes: 5 additions & 1 deletion simulation/src/map/procgen/presets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ pub fn load_parismap(map: &mut Map) {
v.1 |= mi == dst || (n_lanes != 1);
}

for ((src, dst), (fw, bw)) in edges {
let mut edges_keys: Vec<_> = edges.keys().copied().collect();
edges_keys.sort_unstable();

for (src, dst) in edges_keys {
let (fw, bw) = edges[&(src, dst)];
if !fw && !bw {
continue;
}
Expand Down
53 changes: 34 additions & 19 deletions simulation/src/map/terrain.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use crate::map::procgen::heightmap;
use crate::map::procgen::heightmap::tree_density;
use std::ops::Mul;

use flat_spatial::storage::CellIdx;
use flat_spatial::Grid;
use geom::{lerp, pack_height, vec2, Intersect, Radians, Ray3, Vec2, Vec3, AABB};
use prototypes::{Tick, DELTA};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::ops::Mul;

use common::FastSet;
use egui_inspect::egui::ahash::HashSetExt;
use geom::{lerp, pack_height, vec2, Intersect, Radians, Ray3, Vec2, Vec3, AABB};
use prototypes::{Tick, DELTA};

use crate::map::procgen::heightmap;
use crate::map::procgen::heightmap::tree_density;

pub type TerrainChunkID = common::ChunkID_512;

Expand Down Expand Up @@ -97,17 +102,16 @@ impl Environment {
}
});

let mut seen = HashSet::new();
let mut seen = FastSet::new();
for h in to_remove {
let Some(tree) = self.trees.remove(h) else {
let Some(tree) = self.trees.remove_maintain(h) else {
continue;
};
let id = TerrainChunkID::new(tree.pos);
if seen.insert(id) {
f(id);
}
}
self.trees.maintain();
}

pub fn get_chunk(&self, id: TerrainChunkID) -> Option<&Chunk> {
Expand Down Expand Up @@ -279,6 +283,8 @@ impl Environment {

let mut trees = Vec::with_capacity(128);

let tree_storage = self.trees.storage();

for offx in 0..RES_TREES {
for offy in 0..RES_TREES {
let cellpos = vec2(offx as f32, offy as f32) * TCELLW;
Expand All @@ -293,7 +299,11 @@ impl Environment {
let tdens = tree_density(pchunk + sample);

if dens_test < tdens && chunk.height_unchecked(sample) >= 0.0 {
trees.push(Tree::new(pchunk + sample));
let pos = pchunk + sample;
// normalize pos
let cell = tree_storage.cell_id(pos);
let pos = decode_pos(encode_pos(pos, cell), cell);
trees.push(Tree::new(pos));
}
}
}
Expand Down Expand Up @@ -323,15 +333,15 @@ impl Tree {

type SmolTree = u16;

pub fn new_smoltree(pos: Vec2, chunk: (u32, u32)) -> SmolTree {
let diffx = pos.x - (chunk.0 * TREE_GRID_SIZE as u32) as f32;
let diffy = pos.y - (chunk.1 * TREE_GRID_SIZE as u32) as f32;
pub fn encode_pos(pos: Vec2, chunk: CellIdx) -> SmolTree {
let diffx = pos.x - (chunk.0 * TREE_GRID_SIZE as i32) as f32;
let diffy = pos.y - (chunk.1 * TREE_GRID_SIZE as i32) as f32;

((((diffx / TREE_GRID_SIZE as f32) * 256.0) as u8 as u16) << 8)
+ ((diffy / TREE_GRID_SIZE as f32) * 256.0) as u8 as u16
}

pub fn to_pos(encoded: SmolTree, chunk: (u32, u32)) -> Vec2 {
pub fn decode_pos(encoded: SmolTree, chunk: CellIdx) -> Vec2 {
let diffx = (encoded >> 8) as u8;
let diffy = (encoded & 0xFF) as u8;
Vec2 {
Expand All @@ -343,7 +353,7 @@ pub fn to_pos(encoded: SmolTree, chunk: (u32, u32)) -> Vec2 {
#[derive(Serialize, Deserialize)]
struct SerializedEnvironment {
h: Heightmap,
trees: Vec<((u32, u32), Vec<SmolTree>)>,
trees: Vec<(CellIdx, Vec<SmolTree>)>,
}

impl From<SerializedEnvironment> for Environment {
Expand All @@ -355,7 +365,7 @@ impl From<SerializedEnvironment> for Environment {

for (chunk_id, trees) in ser.trees {
for tree in trees {
let tree = Tree::new(to_pos(tree, chunk_id));
let tree = Tree::new(decode_pos(tree, chunk_id));
terrain.trees.insert(tree.pos, tree);
}
}
Expand All @@ -370,11 +380,16 @@ impl From<&Environment> for SerializedEnvironment {
trees: Vec::new(),
};

for (cell_id, chunk) in ter.trees.storage().cells.iter() {
let cell_id = (cell_id.0 as u32, cell_id.1 as u32);
let tree_cells = &ter.trees.storage().cells;

let mut keys = tree_cells.keys().copied().collect::<Vec<_>>();
keys.sort_unstable();

for cell_id in keys {
let chunk = &tree_cells[&cell_id];
let mut smoltrees = Vec::with_capacity(chunk.objs.len());
for (_, tree_pos) in chunk.objs.iter() {
let smol = new_smoltree(*tree_pos, cell_id);
let smol = encode_pos(*tree_pos, cell_id);
smoltrees.push(smol);
}
t.trees.push((cell_id, smoltrees));
Expand Down
Loading

0 comments on commit 4ce3db1

Please sign in to comment.