1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! The callback core of Tickwise.
//!
//! [`DeterminismProbe`] is the single contract between a simulation and
//! Tickwise. Everything else in the kit is built on top of these three
//! callbacks.
/// The single contract between the user's simulation and Tickwise.
///
/// Tickwise is an observer. The user drives their own game loop and calls
/// into the kit, which calls back through this trait. The design rule for
/// this trait is that it must stay simple enough to cross an FFI boundary
/// cleanly: no generics, no closures, plain return values.
///
/// # Examples
///
/// ```
/// use tickwise::{DeterminismProbe, StateDump};
///
/// struct MySim {
/// entity_count: u64,
/// rng_seed: u64,
/// score: u64,
/// }
///
/// impl DeterminismProbe for MySim {
/// fn light_hash(&self) -> u64 {
/// // A cheap digest of desync-critical values, not the whole state.
/// self.entity_count
/// .wrapping_mul(0x9E37_79B9_7F4A_7C15)
/// ^ self.rng_seed.rotate_left(17)
/// ^ self.score
/// }
///
/// fn full_hash(&self) -> u64 {
/// // A real implementation hashes all gameplay state here.
/// self.light_hash()
/// }
///
/// fn state_dump(&self) -> StateDump {
/// StateDump::empty()
/// }
/// }
/// ```
/// A structural, diffable representation of simulation state.
///
/// Conceptually a tree of field paths mapped to typed values, which the
/// diff engine walks to report field-level differences.
///
/// The internal representation is deliberately undecided until M3, so this
/// type is opaque: it can be created empty and passed around, and nothing
/// else. Do not rely on its layout.