nmarshall commited on
Commit
799143f
·
1 Parent(s): d048ff6

gameplay: port recipient numeric payloads

Browse files
crates/urt-gameplay/src/bin/urt-lifecycle.rs CHANGED
@@ -269,6 +269,51 @@ fn run() -> Result<(), String> {
269
  values.join(",")
270
  );
271
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  _ => return Err("unsupported contract or arguments".into()),
273
  }
274
  Ok(())
 
269
  values.join(",")
270
  );
271
  }
272
+ "payload" if w.len() == 44 && w[0] <= 1 && w[1] <= 1 => {
273
+ use urt_gameplay::recipient_payload::{Client, Presence, prepare};
274
+ let client = |v: &[u32], entity_flags: u32| Client {
275
+ pm_flags: v[0],
276
+ health: v[2] as i32,
277
+ protected_flags: v[3],
278
+ score: v[4] as i32,
279
+ deaths: v[5] as i32,
280
+ packed: v[6],
281
+ field308: v[7] as i32,
282
+ ping: v[8] as i32,
283
+ connection: v[9] as i32,
284
+ entered: v[10] as i32,
285
+ class: v[11] as i32,
286
+ awards: v[12] as i32,
287
+ field3692: v[13] as i32,
288
+ special: v[14] as i32,
289
+ follow: v[15] as i32,
290
+ extra_time: v[16] as i32,
291
+ cached_ping: v[17] as i32,
292
+ entity_flags,
293
+ };
294
+ let mut first = client(&w[8..26], w[6]);
295
+ let mut second = client(&w[26..], w[7]);
296
+ let presence = |bits: u32| Presence {
297
+ present: bits & 1 != 0,
298
+ in_use: bits & 2 != 0,
299
+ client: bits & 4 != 0,
300
+ };
301
+ let p = prepare(
302
+ w[0] != 0,
303
+ [presence(w[3]), presence(w[4]), presence(w[5])],
304
+ w[1] != 0,
305
+ w[2] as i32,
306
+ &mut first,
307
+ &mut second,
308
+ );
309
+ let values:Vec<_>=p.iter().map(|r|format!("{{\"score\":{},\"ping\":{},\"minutes\":{},\"status\":{},\"deaths\":{},\"field3692\":{},\"packed_low\":{},\"packed_high\":{},\"field308\":{},\"awards\":{},\"class\":{}}}",r.score,r.ping,r.minutes,r.status,r.deaths,r.field3692,r.packed_low,r.packed_high,r.field308,r.awards,r.class)).collect();
310
+ println!(
311
+ "{{\"payloads\":[{}],\"cached\":[{},{}]}}",
312
+ values.join(","),
313
+ first.cached_ping as u32,
314
+ second.cached_ping as u32
315
+ );
316
+ }
317
  _ => return Err("unsupported contract or arguments".into()),
318
  }
319
  Ok(())
crates/urt-gameplay/src/lib.rs CHANGED
@@ -150,6 +150,7 @@ pub mod method_stats;
150
  pub mod mode10;
151
  pub mod protected;
152
  pub mod recent_awards;
 
153
  pub mod record_clear;
154
  pub mod score;
155
  pub mod stat_totals;
 
150
  pub mod mode10;
151
  pub mod protected;
152
  pub mod recent_awards;
153
+ pub mod recipient_payload;
154
  pub mod record_clear;
155
  pub mod score;
156
  pub mod stat_totals;
crates/urt-gameplay/src/recipient_payload.rs ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //! Numeric179736/180087 projection and cache effects; no string serialization.
2
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
3
+ pub struct Client {
4
+ pub pm_flags: u32,
5
+ pub health: i32,
6
+ pub protected_flags: u32,
7
+ pub score: i32,
8
+ pub deaths: i32,
9
+ pub packed: u32,
10
+ pub field308: i32,
11
+ pub ping: i32,
12
+ pub connection: i32,
13
+ pub entered: i32,
14
+ pub class: i32,
15
+ pub awards: i32,
16
+ pub field3692: i32,
17
+ pub special: i32,
18
+ pub follow: i32,
19
+ pub extra_time: i32,
20
+ pub cached_ping: i32,
21
+ pub entity_flags: u32,
22
+ }
23
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
24
+ pub struct Presence {
25
+ pub present: bool,
26
+ pub in_use: bool,
27
+ pub client: bool,
28
+ }
29
+ impl Presence {
30
+ fn valid(self) -> bool {
31
+ self.present && self.in_use && self.client
32
+ }
33
+ }
34
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
35
+ pub struct Numeric {
36
+ pub score: i32,
37
+ pub ping: i32,
38
+ pub minutes: i32,
39
+ pub status: i32,
40
+ pub deaths: i32,
41
+ pub field3692: i32,
42
+ pub packed_low: i32,
43
+ pub packed_high: i32,
44
+ pub field308: i32,
45
+ pub awards: i32,
46
+ pub class: i32,
47
+ }
48
+ pub fn project(c: &mut Client, now: i32) -> Numeric {
49
+ let mut ping = if c.connection == 2 {
50
+ c.ping.min(999)
51
+ } else {
52
+ 999
53
+ };
54
+ if c.follow != 0 && c.pm_flags & 1024 != 0 {
55
+ ping = c.cached_ping;
56
+ } else {
57
+ c.cached_ping = ping;
58
+ }
59
+ if c.entity_flags & 8 != 0 {
60
+ ping = -1;
61
+ }
62
+ let status = if c.special != 0 {
63
+ 2
64
+ } else if c.follow != 0 || c.health <= 0 || c.protected_flags & 16384 != 0 {
65
+ 1
66
+ } else {
67
+ 0
68
+ };
69
+ Numeric {
70
+ score: c.score,
71
+ ping,
72
+ minutes: now.wrapping_sub(c.entered).wrapping_add(c.extra_time) / 60000,
73
+ status,
74
+ deaths: c.deaths,
75
+ field3692: c.field3692,
76
+ packed_low: (c.packed & 255) as i32,
77
+ packed_high: (c.packed as i32) >> 8,
78
+ field308: c.field308,
79
+ awards: c.awards,
80
+ class: if (0..=20).contains(&c.class) {
81
+ c.class
82
+ } else {
83
+ 0
84
+ },
85
+ }
86
+ }
87
+ /// Single uses participants0/1; pair uses0/1/2. Repeated client storage is
88
+ /// projected in source order through the same mutable first-client state.
89
+ pub fn prepare(
90
+ pair: bool,
91
+ participants: [Presence; 3],
92
+ same_client: bool,
93
+ now: i32,
94
+ first: &mut Client,
95
+ second: &mut Client,
96
+ ) -> Vec<Numeric> {
97
+ let count = if pair { 3 } else { 2 };
98
+ if !participants[..count].iter().all(|p| p.valid()) {
99
+ return vec![];
100
+ }
101
+ let mut payload = vec![project(first, now)];
102
+ if pair {
103
+ payload.push(project(if same_client { first } else { second }, now));
104
+ }
105
+ payload
106
+ }
107
+ #[cfg(test)]
108
+ mod tests {
109
+ use super::*;
110
+ fn client() -> Client {
111
+ Client {
112
+ pm_flags: 0,
113
+ health: 100,
114
+ protected_flags: 0,
115
+ score: 7,
116
+ deaths: 3,
117
+ packed: 0x800001ff,
118
+ field308: 17,
119
+ ping: 1500,
120
+ connection: 2,
121
+ entered: 1000,
122
+ class: 20,
123
+ awards: 4,
124
+ field3692: 9,
125
+ special: 0,
126
+ follow: 0,
127
+ extra_time: 60000,
128
+ cached_ping: 77,
129
+ entity_flags: 0,
130
+ }
131
+ }
132
+ #[test]
133
+ fn cache_freeze_then_publication_override() {
134
+ let mut c = client();
135
+ c.follow = 1;
136
+ c.pm_flags = 1024;
137
+ c.entity_flags = 8;
138
+ assert_eq!(project(&mut c, 121000).ping, -1);
139
+ assert_eq!(c.cached_ping, 77);
140
+ c.follow = 0;
141
+ assert_eq!(project(&mut c, 121000).ping, -1);
142
+ assert_eq!(c.cached_ping, 999);
143
+ c.entity_flags = 0;
144
+ c.ping = -5;
145
+ assert_eq!(project(&mut c, 121000).ping, -5);
146
+ assert_eq!(c.cached_ping, -5);
147
+ }
148
+ #[test]
149
+ fn status_priority_class_and_connection() {
150
+ let mut c = client();
151
+ c.connection = 1;
152
+ c.health = 0;
153
+ c.special = 1;
154
+ c.class = 21;
155
+ let p = project(&mut c, 121000);
156
+ assert_eq!((p.ping, p.status, p.class), (999, 2, 0));
157
+ c.special = 0;
158
+ assert_eq!(project(&mut c, 121000).status, 1);
159
+ c.health = 100;
160
+ assert_eq!(project(&mut c, 121000).status, 0);
161
+ }
162
+ #[test]
163
+ fn wrapped_time_and_signed_packed_word() {
164
+ let mut c = client();
165
+ c.entered = i32::MAX;
166
+ c.extra_time = 0;
167
+ let p = project(&mut c, i32::MIN);
168
+ assert_eq!(p.minutes, 0);
169
+ assert_eq!((p.packed_low, p.packed_high), (255, -8388607));
170
+ }
171
+ #[test]
172
+ fn all_presence_gates_and_alias_order() {
173
+ let valid = Presence {
174
+ present: true,
175
+ in_use: true,
176
+ client: true,
177
+ };
178
+ for j in 0..3 {
179
+ for bad in [
180
+ Presence {
181
+ present: false,
182
+ ..valid
183
+ },
184
+ Presence {
185
+ in_use: false,
186
+ ..valid
187
+ },
188
+ Presence {
189
+ client: false,
190
+ ..valid
191
+ },
192
+ ] {
193
+ let mut ps = [valid; 3];
194
+ ps[j] = bad;
195
+ let mut a = client();
196
+ let mut b = client();
197
+ let before = (a, b);
198
+ assert!(prepare(true, ps, false, 121000, &mut a, &mut b).is_empty());
199
+ assert_eq!((a, b), before);
200
+ }
201
+ }
202
+ let mut a = client();
203
+ let mut b = client();
204
+ let before = b;
205
+ let p = prepare(true, [valid; 3], true, 121000, &mut a, &mut b);
206
+ assert_eq!(p.len(), 2);
207
+ assert_eq!(p[0], p[1]);
208
+ assert_eq!(b, before);
209
+ }
210
+ }
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -958,3 +958,6 @@ Research progress: RE-765 [report](research/RUST_STAT_TOTALS.md); original asset
958
 
959
 
960
  Research progress: RE-766 [report](research/RUST_RECENT_AWARDS.md); original assets unchanged.
 
 
 
 
958
 
959
 
960
  Research progress: RE-766 [report](research/RUST_RECENT_AWARDS.md); original assets unchanged.
961
+
962
+
963
+ Research progress: RE-767 [report](research/RUST_RECIPIENT_PAYLOAD.md); original assets unchanged.
docs/FINDINGS.md CHANGED
@@ -1304,3 +1304,6 @@ RE-725–734 batch: [health, contact and Rust impulse findings](research/QVM_HEA
1304
 
1305
 
1306
  **RE-766 — Rust recent record awards timestamp gates and alias storage:** Rust recent-record awards match68 source fixtures, preserving future ages, wrapped clocks, ID gates and index64 award/count aliasing. [Evidence](research/RUST_RECENT_AWARDS.md).
 
 
 
 
1304
 
1305
 
1306
  **RE-766 — Rust recent record awards timestamp gates and alias storage:** Rust recent-record awards match68 source fixtures, preserving future ages, wrapped clocks, ID gates and index64 award/count aliasing. [Evidence](research/RUST_RECENT_AWARDS.md).
1307
+
1308
+
1309
+ **RE-767 — Rust recipient numeric projection and reporting batch handoff:** Rust recipient participant gates, numeric fields and cache effects match72 single and73 pair source fixtures, including self aliases. [Evidence](research/RUST_RECIPIENT_PAYLOAD.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1299,3 +1299,9 @@ Research progress: RE-765 [report](research/RUST_STAT_TOTALS.md); full native/pl
1299
 
1300
 
1301
  Research progress: RE-766 [report](research/RUST_RECENT_AWARDS.md); full native/platform/game scope remains open.
 
 
 
 
 
 
 
1299
 
1300
 
1301
  Research progress: RE-766 [report](research/RUST_RECENT_AWARDS.md); full native/platform/game scope remains open.
1302
+
1303
+
1304
+ Research progress: RE-767 [report](research/RUST_RECIPIENT_PAYLOAD.md); full native/platform/game scope remains open.
1305
+
1306
+
1307
+ RE-758–767 batch validation:1473 Python and80 Rust tests pass after ten independently committed tasks. Totals/ratios, team counters, recent awards, recipient numeric bodies and ranking setup have finite source evidence; Rust adds totals/ratios, recent-award records and recipient numeric/cache contracts. Ranking sort/publication, real serialization, full runtime ownership, native parity, Linux and interactive Bevy/Solari acceptance remain open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -1486,3 +1486,15 @@ Rust totals and normalized ratios match80 total,28 ratio and38 signed-divisor so
1486
  ## 2026-10-21 — RE-766 Rust recent record awards timestamp gates and alias storage
1487
 
1488
  Rust recent-record awards match68 source fixtures, preserving future ages, wrapped clocks, ID gates and index64 award/count aliasing. Focused `cargo xtask test-group gameplay` passes (28 Python tests); selected reports regenerate twice byte-identically, with storage, links and whitespace guards. New report: 143080 bytes. Full-suite validation is deferred to the tenth batch commit. Rust gameplay crate changed; focused gameplay regenerates nine parity contracts and scoped package tests. Full workspace suite remains deferred to commit ten.
 
 
 
 
 
 
 
 
 
 
 
 
 
1486
  ## 2026-10-21 — RE-766 Rust recent record awards timestamp gates and alias storage
1487
 
1488
  Rust recent-record awards match68 source fixtures, preserving future ages, wrapped clocks, ID gates and index64 award/count aliasing. Focused `cargo xtask test-group gameplay` passes (28 Python tests); selected reports regenerate twice byte-identically, with storage, links and whitespace guards. New report: 143080 bytes. Full-suite validation is deferred to the tenth batch commit. Rust gameplay crate changed; focused gameplay regenerates nine parity contracts and scoped package tests. Full workspace suite remains deferred to commit ten.
1489
+
1490
+
1491
+ ## 2026-10-21 — RE-767 Rust recipient numeric projection and reporting batch handoff
1492
+
1493
+ Rust recipient participant gates, numeric fields and cache effects match72 single and73 pair source fixtures, including self aliases. Focused `cargo xtask test-group gameplay` passes (31 Python tests); selected reports regenerate twice byte-identically, with storage, links and whitespace guards. New report: 312484 bytes. Full-suite validation is deferred to the tenth batch commit. Rust gameplay and registry integration tests changed. Focused gameplay covers ten parity reports and scoped package tests. The full suite runs once after commit ten, then documentation is amended with its outcome.
1494
+
1495
+ Additional RE-767 selector verification: scoped `cargo test --offline --package xtask` passes28 tests. Registry integration confirms totals, recent-award and recipient-payload module edits select their parity reports/tests and the impulse-owned package test-count metadata without fallback.
1496
+
1497
+
1498
+ ## 2026-10-21 — RE-758–767 full batch validation
1499
+
1500
+ Exactly ten task commits follow baseline `27a7f96f`. Every completed task passed focused `cargo xtask test-group` checks before its final commit; no full suite ran between those task commits. After commit ten, the single `cargo xtask test-full --since 27a7f96f` run passed1473 Python tests (519+357+352+245) and80 Rust tests (10 assets,42 gameplay,28 xtask). Seventeen affected report producers regenerated twice byte-identically. Storage policy, documentation links across326 files and whitespace guards passed. Generated outputs matched committed reports and the working tree was clean after validation. The tenth commit is amended only to record this result. Original assets remain unchanged. Ranking sort/publication, actual text serialization, statistic leaderboards, full lifecycle/runtime composition, original native parity, Linux and interactive Bevy/Solari gameplay remain open.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -1311,7 +1311,7 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1311
  - [x] RE-755 Rust method classification wrapping counters and reporting intent ([report](research/RUST_METHOD_STATS.md)).
1312
 
1313
 
1314
- - [ ] RE-757 Recover ranking/recipient/stat-report children, compose actual lifecycle from full health/contact/shared-pellet entry, establish owned Rust runtime events/counters and validate native/macOS/Linux Bevy/Solari gameplay. See the [next tasks](research/QVM_SCORE_STATS_RUST_BATCH.md#remaining-limits-and-next-tasks).
1315
 
1316
 
1317
  - [x] RE-756 Rust cross client clearing and score statistics batch handoff ([report](research/RUST_CROSS_CLIENT_CLEAR.md)).
@@ -1342,3 +1342,9 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1342
 
1343
 
1344
  - [x] RE-766 Rust recent record awards timestamp gates and alias storage ([report](research/RUST_RECENT_AWARDS.md)).
 
 
 
 
 
 
 
1311
  - [x] RE-755 Rust method classification wrapping counters and reporting intent ([report](research/RUST_METHOD_STATS.md)).
1312
 
1313
 
1314
+ - [ ] RE-757 Partially advanced by RE-758–767 ([batch](research/QVM_REPORTING_RUST_BATCH.md)); recover remaining ranking/recipient/stat-report children, compose actual lifecycle from full health/contact/shared-pellet entry, establish owned Rust runtime events/counters and validate native/macOS/Linux Bevy/Solari gameplay. See the [next tasks](research/QVM_SCORE_STATS_RUST_BATCH.md#remaining-limits-and-next-tasks).
1315
 
1316
 
1317
  - [x] RE-756 Rust cross client clearing and score statistics batch handoff ([report](research/RUST_CROSS_CLIENT_CLEAR.md)).
 
1342
 
1343
 
1344
  - [x] RE-766 Rust recent record awards timestamp gates and alias storage ([report](research/RUST_RECENT_AWARDS.md)).
1345
+
1346
+
1347
+ - [ ] RE-768 Complete ranking sort/publication and recipient serialization/stat-leaderboard children, compose recovered effects through full health/contact/shared-pellet callers, establish owned Rust runtime state/events and validate original native/macOS/Linux Bevy/Solari gameplay. See the [next tasks](research/QVM_REPORTING_RUST_BATCH.md#remaining-limits-and-next-tasks).
1348
+
1349
+
1350
+ - [x] RE-767 Rust recipient numeric projection and reporting batch handoff ([report](research/RUST_RECIPIENT_PAYLOAD.md)).
docs/TESTING.md CHANGED
@@ -98,3 +98,7 @@ Gameplay module changes have explicit edges to their parity adapters and to the
98
  ## Score, statistics and recipient groups
99
 
100
  Use `cargo xtask test-group score`, `notifications`, `stat-slots`, `stat-updates`, or `record-clear` for individual recovered contracts. `resolved-protected` and `resolved-mode10` exercise actual child composition; `health` follows shared dependencies for a broader focused regression. `gameplay` now covers seven Rust parity contracts, including score, method counters and cross-client clearing. Explicit module edges also refresh the impulse-owned scoped package test-count metadata. The registry integration test verifies those edges. Continue focused checks before each task commit and one `test-full --since <batch-base>` run after ten commits. See the [score/statistics batch](research/QVM_SCORE_STATS_RUST_BATCH.md) for fixture and native boundaries.
 
 
 
 
 
98
  ## Score, statistics and recipient groups
99
 
100
  Use `cargo xtask test-group score`, `notifications`, `stat-slots`, `stat-updates`, or `record-clear` for individual recovered contracts. `resolved-protected` and `resolved-mode10` exercise actual child composition; `health` follows shared dependencies for a broader focused regression. `gameplay` now covers seven Rust parity contracts, including score, method counters and cross-client clearing. Explicit module edges also refresh the impulse-owned scoped package test-count metadata. The registry integration test verifies those edges. Continue focused checks before each task commit and one `test-full --since <batch-base>` run after ten commits. See the [score/statistics batch](research/QVM_SCORE_STATS_RUST_BATCH.md) for fixture and native boundaries.
101
+
102
+ ## Totals, reporting and ranking setup groups
103
+
104
+ Focused groups now include `stat-totals`, `stat-ratios`, `team-counters`, `recent-awards`, `recipient-single`, `recipient-pair` and `ranking-setup`. Recipient groups cover numeric source body boundaries with explicit text/native stand-ins. `gameplay` runs ten Rust parity adapters and scoped gameplay package tests, including totals/ratios, recent awards and recipient projection. Explicit module edges and registry integration tests also refresh the package test-count metadata. Continue focused checks before each commit and one full-suite run after ten commits. See the [reporting batch](research/QVM_REPORTING_RUST_BATCH.md) for evidence limits.
docs/research/QVM_REPORTING_RUST_BATCH.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Totals, reporting and ranking setup batch
2
+
3
+ RE-758–767 continues from `27a7f96f` with ten independently committed tasks. The objective remains complete Rust/Bevy/Solari gameplay on macOS and Linux. This batch advances bounded QVM contracts and three Rust simulation primitives; it does not establish original executable parity or a playable runtime.
4
+
5
+ | Task | Scope | Evidence |
6
+ | --- | --- | --- |
7
+ | RE-758 | Complete statistic total functions and20-slot field inclusion | [Totals](QVM_STAT_TOTALS.md) |
8
+ | RE-759 | Complete divisor and normalized ratio publication with actual totals | [Ratios](QVM_STAT_RATIOS.md) |
9
+ | RE-760 | Complete same-team pair counters and participant gates | [Team counters](QVM_TEAM_COUNTERS.md) |
10
+ | RE-761 | Complete recent-record awards with timestamp/count/index64 aliases | [Awards](QVM_RECENT_AWARDS.md) |
11
+ | RE-762 | Single recipient numeric body, gates and cached ping | [Single payload](QVM_RECIPIENT_SINGLE.md) |
12
+ | RE-763 | Pair recipient numeric body, gates and cache aliases | [Pair payload](QVM_RECIPIENT_PAIR.md) |
13
+ | RE-764 | Ranking reset/client scan through the sorting boundary | [Ranking setup](QVM_RANKING_SETUP.md) |
14
+ | RE-765 | Rust totals, divisor and ratio primitives | [Rust totals](RUST_STAT_TOTALS.md) |
15
+ | RE-766 | Rust recent-record awards and log ID intents | [Rust awards](RUST_RECENT_AWARDS.md) |
16
+ | RE-767 | Rust recipient gates, numeric fields and cache effects | [Rust payload](RUST_RECIPIENT_PAYLOAD.md) |
17
+
18
+ The totals read20 slot counters although method classification currently maps19 slots. First legacy totals exclude indices13/17; second totals exclude17. Addition wraps. The divisor scans down from the signed maximum: mixed signs can still reduce, but a zero input or nonpositive signed maximum returns1. Rust uses Euclid with the same guards. Ratio publication preserves source float32 conversion/division order and zero-denominator behavior.
19
+
20
+ Recent-record awards accept index64 and preserve the alias between pair64 count and the3032 award word. Eligibility uses count at least40 and wrapped signed age at most30000, with no added nonnegative-age condition. Only64 recipient slots are scanned. Logs remain returned/captured intents rather than executed formatting.
21
+
22
+ Recipient numeric bodies cap ping above999, retain negative ping, optionally reuse follow-cache data and apply the entity-bit publication override after cache handling. Status precedence, wrapped elapsed minutes, signed packed fields and class bounds0..20 are independently checked. Pair aliases are processed in source order. Initial unaligned literal-copy setup is excluded from the source body boundaries; strlen lengths are supplied and copy/format/send effects remain captured. These are numeric payload/cache contracts, not serialized-text or networking parity.
23
+
24
+ Ranking5864 setup resets and counts clients, stores a scan-order enrolled list and the first two active slots, and stops before sorting35095/comparator5441. Sort ordering, tie handling, rank assignment and publication remain open. The first two active slots include entity-bit8 clients; human team counts exclude them.
25
+
26
+ ## Validation cadence
27
+
28
+ Focused `cargo xtask test-group` checks precede each task commit. Selected reports regenerate twice, followed by affected tests, storage/link checks and whitespace guards. Single-recipient regression also checks compatibility of the shared pair helper. Rust gameplay groups run scoped package tests and ten parity reports. After commit ten, the single `cargo xtask test-full --since 27a7f96f` run passed1473 Python tests and80 Rust tests (10 assets,42 gameplay,28 xtask). Seventeen affected report producers regenerated twice byte-identically; storage, documentation links across326 files and whitespace guards passed. Generated outputs matched committed reports and the working tree was clean. This documentation-only result is recorded by amending the tenth commit.
29
+
30
+ ## Remaining limits and next tasks
31
+
32
+ 1. Recover sorting35095/comparator5441 and the remaining5864 ranking assignment/publication branches.
33
+ 2. Execute the recipient literal/copy/strlen/formatting boundaries with original text, and verify exact serialized command payloads.
34
+ 3. Recover283622 statistic leaderboard selection and its284555/284610 dependencies.
35
+ 4. Compose actual279720/279586/285227 and recipient numeric bodies into the existing complete stat/lifecycle callers while retaining unresolved boundaries.
36
+ 5. Compose actual protected completion from full health mutation, including null attacker and original low-page reads.
37
+ 6. Extend actual health/contact/shared293732 and successful295018 pellet aggregation through all recovered lifecycle state.
38
+ 7. Establish owned Rust counters, events, record lifetimes, recipient identities, inventory resets and obituary serialization in a runtime.
39
+ 8. Continue original native executable/DLL/library parity and loader ownership.
40
+ 9. Connect assets, simulation and world collision to macOS/Linux Bevy runtime validation.
41
+ 10. Port rendering/materials to Solari and verify interactive full-game acceptance on both platforms.
42
+
43
+ RE-757 remains partially advanced. Native, Linux, rendering and full gameplay acceptance remain open.
docs/research/README.md CHANGED
@@ -894,3 +894,6 @@ The extension collector adds five focused tests for native tail calls, command-r
894
 
895
 
896
  [RE-766: Rust recent record awards timestamp gates and alias storage](RUST_RECENT_AWARDS.md).
 
 
 
 
894
 
895
 
896
  [RE-766: Rust recent record awards timestamp gates and alias storage](RUST_RECENT_AWARDS.md).
897
+
898
+
899
+ [RE-767: Rust recipient numeric projection and reporting batch handoff](RUST_RECIPIENT_PAYLOAD.md).
docs/research/RUST_RECIPIENT_PAYLOAD.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Rust recipient numeric projection and reporting batch handoff
2
+
3
+ RE-767. Reproduce with `python3 scripts/analyze_rust_recipient_payload.py`; validate with `cargo xtask test-group gameplay`. See the [machine report](rust-recipient-payload.json).
4
+
5
+ Added typed presence/client/numeric payload contracts, source-order cache projection and pointer/client/in-use gates. Four Rust unit tests cover cache freezing/override, negative ping, status/class/connection rules, wrapped time, signed packed fields and all participant gates/alias order. Registry integration verifies all three new module selections. See the [batch handoff](QVM_REPORTING_RUST_BATCH.md) for literal-copy/serialization/native boundaries and next tasks.
6
+
7
+ No original executable/full VM/native world collision or rendering runs. Supplied callback/native responses and fixture domains remain explicit; complete macOS/Linux Rust/Bevy/Solari gameplay remains open.
docs/research/rust-health-impulse.json CHANGED
@@ -2985,7 +2985,7 @@
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
- "rust_unit_tests": 38,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
 
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
+ "rust_unit_tests": 42,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
docs/research/rust-recipient-payload.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_rust_recipient_payload.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Rust numeric recipient projection/cache/gates against both QVM bodies."""
3
+ import hashlib,json
4
+ from pathlib import Path
5
+ from rust_lifecycle_support import binary,result
6
+ from qvm_recipient_payload_support import FIELDS
7
+ from analyze_qvm import write_json
8
+ KEYS=['score','ping','minutes','status','deaths','field3692','packed_low','packed_high','field308','awards','class']
9
+
10
+ def presence(present,active,client):return int(bool(present))+2*int(bool(active))+4*int(bool(client))
11
+
12
+ def main():
13
+ b=binary();paths=[Path('docs/research/qvm-recipient-single.json'),Path('docs/research/qvm-recipient-pair.json')];sources={};rows=[]
14
+ for pair,path in enumerate(paths):
15
+ data=path.read_bytes();sources[str(path)]=hashlib.sha256(data).hexdigest()
16
+ for p in json.loads(data)['probes']:
17
+ a=p['input'];first=presence(not a.get('null'),a.get('active',1),a.get('client',True));recipient=presence(not a.get('null_recipient'),a.get('recipient_active',1),a.get('recipient_client',True));second=presence(not a.get('null_second'),a.get('second_active',1),a.get('second_client',True));ps=[first,second,recipient] if pair else [first,recipient,7];args=[pair,int(a.get('self',False)),a.get('now',121000),*ps,a.get('entity_flags',0),a.get('second_entity_flags',0),*[c[str(o)] for c in p['initial_clients'] for o in FIELDS]];formats=[r for r in p['requests'] if r['target']==70458];payloads=[]
18
+ if formats:
19
+ for offset in ([16,68] if pair else [16]):payloads.append({key:formats[0]['arguments'][str(offset+4*j)] for j,key in enumerate(KEYS)})
20
+ expected={'payloads':payloads,'cached':[c['6160'] for c in p['final_clients']]};rows.append({'pair':bool(pair),'input':a,'initial_clients':p['initial_clients'],'source':expected,'rust':result(b,'payload',args)})
21
+ write_json(Path('docs/research/rust-recipient-payload.json'),{'source_reports':sources,'probes':rows,'limits':'Rust numeric participant gates, payload fields and ping-cache effects match72 single and73 pair source fixtures. Alias order preserved. QVM literal-copy setup and actual string/copy/format/send effects remain excluded/captured; no serialized/network/native/platform parity.'})
22
+ if __name__=='__main__':main()
scripts/test_rust_recipient_payload.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib,json,unittest
2
+ from pathlib import Path
3
+ from test_qvm_recipient_single import numeric
4
+ class RustRecipientPayloadTests(unittest.TestCase):
5
+ @classmethod
6
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/rust-recipient-payload.json').read_text())
7
+ def test_all_source_numeric_fields_cache_and_presence_gates(self):
8
+ self.assertEqual(len(self.r['probes']),145)
9
+ for p in self.r['probes']:self.assertEqual(p['rust'],p['source'],(p['pair'],p['input']))
10
+ def test_independent_active_projection_and_alias_order(self):
11
+ for p in self.r['probes']:
12
+ if not p['source']['payloads']:continue
13
+ a=p['input'];clients=[dict(c) for c in p['initial_clients']];fields=[]
14
+ for j in range(2 if p['pair'] else 1):
15
+ selected=0 if j==0 or a.get('self') else 1;cache,row=numeric(clients[selected],a.get('entity_flags' if selected==0 else 'second_entity_flags',0),a.get('now',121000));clients[selected]['6160']=cache&0xffffffff;fields.append(row)
16
+ self.assertEqual(p['rust'],{'payloads':fields,'cached':[c['6160'] for c in clients]})
17
+ def test_source_identity(self):
18
+ for path,sha in self.r['source_reports'].items():self.assertEqual(hashlib.sha256(Path(path).read_bytes()).hexdigest(),sha)
19
+ if __name__=='__main__':unittest.main()
xtask/checks.tsv CHANGED
@@ -1313,3 +1313,13 @@ edge crates/urt-gameplay/src/recent_awards.rs scripts/analyze_rust_health_impuls
1313
  group gameplay scripts/analyze_rust_recent_awards.py
1314
  report scripts/analyze_rust_recent_awards.py docs/research/rust-recent-awards.json
1315
  edge docs/research/rust-recent-awards.json scripts/test_rust_recent_awards.py
 
 
 
 
 
 
 
 
 
 
 
1313
  group gameplay scripts/analyze_rust_recent_awards.py
1314
  report scripts/analyze_rust_recent_awards.py docs/research/rust-recent-awards.json
1315
  edge docs/research/rust-recent-awards.json scripts/test_rust_recent_awards.py
1316
+ edge Cargo.toml scripts/analyze_rust_recipient_payload.py
1317
+ edge Cargo.lock scripts/analyze_rust_recipient_payload.py
1318
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_recipient_payload.py
1319
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_recipient_payload.py
1320
+ edge crates/urt-gameplay/src/recipient_payload.rs scripts/analyze_rust_recipient_payload.py
1321
+ edge crates/urt-gameplay/src/bin/urt-lifecycle.rs scripts/analyze_rust_recipient_payload.py
1322
+ edge crates/urt-gameplay/src/recipient_payload.rs scripts/analyze_rust_health_impulse.py
1323
+ group gameplay scripts/analyze_rust_recipient_payload.py
1324
+ report scripts/analyze_rust_recipient_payload.py docs/research/rust-recipient-payload.json
1325
+ edge docs/research/rust-recipient-payload.json scripts/test_rust_recipient_payload.py
xtask/src/lib.rs CHANGED
@@ -1037,6 +1037,9 @@ mod integration_tests {
1037
  ("score", "score_update"),
1038
  ("method_stats", "method_stats"),
1039
  ("record_clear", "cross_client_clear"),
 
 
 
1040
  ] {
1041
  let selected = plan(
1042
  &g,
 
1037
  ("score", "score_update"),
1038
  ("method_stats", "method_stats"),
1039
  ("record_clear", "cross_client_clear"),
1040
+ ("stat_totals", "stat_totals"),
1041
+ ("recent_awards", "recent_awards"),
1042
+ ("recipient_payload", "recipient_payload"),
1043
  ] {
1044
  let selected = plan(
1045
  &g,