Skip to content

Commit 4ce3db1

Browse files
committed
fix determinism around trees and test
1 parent df4c776 commit 4ce3db1

File tree

13 files changed

+1065
-12783
lines changed

13 files changed

+1065
-12783
lines changed

.github/workflows/rust-build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ jobs:
3131
- name: Check
3232
run: cargo check --verbose --workspace --tests --examples --features multiplayer
3333
- name: Run tests
34-
run: cargo test --verbose --workspace --features multiplayer
34+
run: cargo test --verbose --workspace --features multiplayer --test-threads=1

Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

prototypes/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![warn(clippy::iter_over_hash_type)]
2+
13
use common::TransparentMap;
24
use geom::Vec2;
35
use mlua::{FromLua, Table};

prototypes/src/validation.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::iter_over_hash_type)]
2+
13
use thiserror::Error;
24

35
use common::error::MultiError;

simulation/src/economy/market.rs

+2
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ fn calculate_prices(price_multiplier: f32) -> BTreeMap<ItemID, Money> {
383383
* company.n_workers as f64
384384
* WORKER_CONSUMPTION_PER_MINUTE;
385385

386+
dbg!(price_consumption, price_workers, qty);
387+
386388
let newprice = (price_consumption
387389
+ Money::new_inner((price_workers.inner() as f32 * price_multiplier) as i64))
388390
/ qty;

simulation/src/init.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
use serde::de::DeserializeOwned;
2+
use serde::Serialize;
3+
4+
#[allow(unused_imports)]
5+
use common::saveload::{Bincode, Encoder, JSONPretty, JSON};
6+
use prototypes::{GameTime, Tick};
7+
18
use crate::economy::{market_update, EcoStats, Government, Market};
29
use crate::map::Map;
310
use crate::map_dynamic::{
@@ -22,10 +29,6 @@ use crate::{
2229
add_souls_to_empty_buildings, utils, ParCommandBuffer, RandProvider, Replay, RunnableSystem,
2330
Simulation, SimulationOptions, RNG_SEED,
2431
};
25-
use common::saveload::{Bincode, Encoder, JSON};
26-
use prototypes::{GameTime, Tick};
27-
use serde::de::DeserializeOwned;
28-
use serde::Serialize;
2932

3033
pub fn init() {
3134
//crate::rerun::init_rerun();

simulation/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(clippy::too_many_arguments)]
22
#![allow(clippy::type_complexity)]
3+
#![warn(clippy::iter_over_hash_type)]
34

45
use crate::init::{GSYSTEMS, INIT_FUNCS, SAVELOAD_FUNCS};
56
use crate::map::{BuildingKind, Map};

simulation/src/map/objects/intersection.rs

+43-40
Original file line numberDiff line numberDiff line change
@@ -110,53 +110,56 @@ impl Intersection {
110110
}
111111

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

118-
if let [] = *self.roads {
119-
return;
120-
}
121-
122-
if let [r1_id] = *self.roads {
123-
let r = &mut roads[r1_id];
124-
r.set_interface(id, Self::empty_interface(r.width));
125-
return;
126-
}
127-
128-
if let [r1_id, r2_id] = *self.roads {
129-
let (r1, r2) = (&roads[r1_id], &roads[r2_id]);
130-
let (dir1, dir2) = (r1.dir_from(id), r2.dir_from(id));
131-
let (r1w, r2w) = (r1.width, r2.width);
132-
let elbow = (dir1 + dir2) * 0.5;
133-
134-
if elbow.mag() < 0.001 {
135-
roads[r1_id].set_interface(id, 1.0);
136-
roads[r2_id].set_interface(id, 1.0);
116+
match *self.roads {
117+
[] => return,
118+
[r1_id] => {
119+
let r = &mut roads[r1_id];
120+
r.set_interface(id, Self::empty_interface(r.width));
137121
return;
138122
}
123+
[r1_id, r2_id] => {
124+
let (r1, r2) = (&roads[r1_id], &roads[r2_id]);
125+
let (dir1, dir2) = (r1.dir_from(id), r2.dir_from(id));
126+
let (r1w, r2w) = (r1.width, r2.width);
127+
let elbow = (dir1 + dir2) * 0.5;
128+
129+
if elbow.mag() < 0.001 {
130+
roads[r1_id].set_interface(id, 1.0);
131+
roads[r2_id].set_interface(id, 1.0);
132+
return;
133+
}
139134

140-
let ray1 = Ray::new(
141-
self.pos.xy()
142-
+ dir1.perpendicular() * dir1.perpendicular().dot(elbow).signum() * r1w * 0.5,
143-
dir1,
144-
);
145-
let ray2 = Ray::new(
146-
self.pos.xy()
147-
+ dir2.perpendicular() * dir2.perpendicular().dot(elbow).signum() * r2w * 0.5,
148-
dir2,
149-
);
150-
151-
let Some((dist_a, dist_b)) = ray1.both_dist_to_inter(&ray2) else {
152-
roads[r1_id].set_interface(id, Self::empty_interface(r1w));
153-
roads[r2_id].set_interface(id, Self::empty_interface(r2w));
135+
let ray1 = Ray::new(
136+
self.pos.xy()
137+
+ dir1.perpendicular()
138+
* dir1.perpendicular().dot(elbow).signum()
139+
* r1w
140+
* 0.5,
141+
dir1,
142+
);
143+
let ray2 = Ray::new(
144+
self.pos.xy()
145+
+ dir2.perpendicular()
146+
* dir2.perpendicular().dot(elbow).signum()
147+
* r2w
148+
* 0.5,
149+
dir2,
150+
);
151+
152+
let Some((dist_a, dist_b)) = ray1.both_dist_to_inter(&ray2) else {
153+
roads[r1_id].set_interface(id, Self::empty_interface(r1w));
154+
roads[r2_id].set_interface(id, Self::empty_interface(r2w));
155+
return;
156+
};
157+
158+
roads[r1_id].set_interface(id, dist_a);
159+
roads[r2_id].set_interface(id, dist_b);
154160
return;
155-
};
156-
157-
roads[r1_id].set_interface(id, dist_a);
158-
roads[r2_id].set_interface(id, dist_b);
159-
return;
161+
}
162+
_ => {}
160163
}
161164

162165
for &r in &self.roads {

simulation/src/map/procgen/presets.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ pub fn load_parismap(map: &mut Map) {
104104
v.1 |= mi == dst || (n_lanes != 1);
105105
}
106106

107-
for ((src, dst), (fw, bw)) in edges {
107+
let mut edges_keys: Vec<_> = edges.keys().copied().collect();
108+
edges_keys.sort_unstable();
109+
110+
for (src, dst) in edges_keys {
111+
let (fw, bw) = edges[&(src, dst)];
108112
if !fw && !bw {
109113
continue;
110114
}

simulation/src/map/terrain.rs

+34-19
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
use crate::map::procgen::heightmap;
2-
use crate::map::procgen::heightmap::tree_density;
1+
use std::ops::Mul;
2+
3+
use flat_spatial::storage::CellIdx;
34
use flat_spatial::Grid;
4-
use geom::{lerp, pack_height, vec2, Intersect, Radians, Ray3, Vec2, Vec3, AABB};
5-
use prototypes::{Tick, DELTA};
65
use rayon::iter::{IntoParallelIterator, ParallelIterator};
76
use serde::{Deserialize, Serialize};
8-
use std::collections::HashSet;
9-
use std::ops::Mul;
7+
8+
use common::FastSet;
9+
use egui_inspect::egui::ahash::HashSetExt;
10+
use geom::{lerp, pack_height, vec2, Intersect, Radians, Ray3, Vec2, Vec3, AABB};
11+
use prototypes::{Tick, DELTA};
12+
13+
use crate::map::procgen::heightmap;
14+
use crate::map::procgen::heightmap::tree_density;
1015

1116
pub type TerrainChunkID = common::ChunkID_512;
1217

@@ -97,17 +102,16 @@ impl Environment {
97102
}
98103
});
99104

100-
let mut seen = HashSet::new();
105+
let mut seen = FastSet::new();
101106
for h in to_remove {
102-
let Some(tree) = self.trees.remove(h) else {
107+
let Some(tree) = self.trees.remove_maintain(h) else {
103108
continue;
104109
};
105110
let id = TerrainChunkID::new(tree.pos);
106111
if seen.insert(id) {
107112
f(id);
108113
}
109114
}
110-
self.trees.maintain();
111115
}
112116

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

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

286+
let tree_storage = self.trees.storage();
287+
282288
for offx in 0..RES_TREES {
283289
for offy in 0..RES_TREES {
284290
let cellpos = vec2(offx as f32, offy as f32) * TCELLW;
@@ -293,7 +299,11 @@ impl Environment {
293299
let tdens = tree_density(pchunk + sample);
294300

295301
if dens_test < tdens && chunk.height_unchecked(sample) >= 0.0 {
296-
trees.push(Tree::new(pchunk + sample));
302+
let pos = pchunk + sample;
303+
// normalize pos
304+
let cell = tree_storage.cell_id(pos);
305+
let pos = decode_pos(encode_pos(pos, cell), cell);
306+
trees.push(Tree::new(pos));
297307
}
298308
}
299309
}
@@ -323,15 +333,15 @@ impl Tree {
323333

324334
type SmolTree = u16;
325335

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

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

334-
pub fn to_pos(encoded: SmolTree, chunk: (u32, u32)) -> Vec2 {
344+
pub fn decode_pos(encoded: SmolTree, chunk: CellIdx) -> Vec2 {
335345
let diffx = (encoded >> 8) as u8;
336346
let diffy = (encoded & 0xFF) as u8;
337347
Vec2 {
@@ -343,7 +353,7 @@ pub fn to_pos(encoded: SmolTree, chunk: (u32, u32)) -> Vec2 {
343353
#[derive(Serialize, Deserialize)]
344354
struct SerializedEnvironment {
345355
h: Heightmap,
346-
trees: Vec<((u32, u32), Vec<SmolTree>)>,
356+
trees: Vec<(CellIdx, Vec<SmolTree>)>,
347357
}
348358

349359
impl From<SerializedEnvironment> for Environment {
@@ -355,7 +365,7 @@ impl From<SerializedEnvironment> for Environment {
355365

356366
for (chunk_id, trees) in ser.trees {
357367
for tree in trees {
358-
let tree = Tree::new(to_pos(tree, chunk_id));
368+
let tree = Tree::new(decode_pos(tree, chunk_id));
359369
terrain.trees.insert(tree.pos, tree);
360370
}
361371
}
@@ -370,11 +380,16 @@ impl From<&Environment> for SerializedEnvironment {
370380
trees: Vec::new(),
371381
};
372382

373-
for (cell_id, chunk) in ter.trees.storage().cells.iter() {
374-
let cell_id = (cell_id.0 as u32, cell_id.1 as u32);
383+
let tree_cells = &ter.trees.storage().cells;
384+
385+
let mut keys = tree_cells.keys().copied().collect::<Vec<_>>();
386+
keys.sort_unstable();
387+
388+
for cell_id in keys {
389+
let chunk = &tree_cells[&cell_id];
375390
let mut smoltrees = Vec::with_capacity(chunk.objs.len());
376391
for (_, tree_pos) in chunk.objs.iter() {
377-
let smol = new_smoltree(*tree_pos, cell_id);
392+
let smol = encode_pos(*tree_pos, cell_id);
378393
smoltrees.push(smol);
379394
}
380395
t.trees.push((cell_id, smoltrees));

0 commit comments

Comments
 (0)