nmarshall commited on
Commit
1332be7
·
1 Parent(s): 6a6674d

gameplay: port early client routing policy

Browse files
crates/urt-gameplay/src/bin/urt-client-processing.rs ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use urt_gameplay::client_processing::*;
2
+ fn hex(v: &[u8]) -> String {
3
+ v.iter().map(|v| format!("{v:02x}")).collect()
4
+ }
5
+ fn list(v: &[u32]) -> String {
6
+ format!(
7
+ "[{}]",
8
+ v.iter()
9
+ .map(|v| v.to_string())
10
+ .collect::<Vec<_>>()
11
+ .join(",")
12
+ )
13
+ }
14
+ fn events(v: &[ProcessingEffect]) -> String {
15
+ v.iter()
16
+ .map(|e| match e {
17
+ ProcessingEffect::Intermission { slot } => {
18
+ format!(r#"{{"op":"intermission","slot":{}}}"#, slot)
19
+ }
20
+ ProcessingEffect::Spectator { slot, command_time } => format!(
21
+ r#"{{"op":"spectator","slot":{},"command_time":{}}}"#,
22
+ slot, command_time
23
+ ),
24
+ ProcessingEffect::Team { slot, text, force } => format!(
25
+ r#"{{"op":"team","slot":{},"text":"{}","force":{}}}"#,
26
+ slot,
27
+ hex(text),
28
+ force
29
+ ),
30
+ ProcessingEffect::Send { slot, text } => {
31
+ format!(r#"{{"op":"send","slot":{},"text":"{}"}}"#, slot, hex(text))
32
+ }
33
+ ProcessingEffect::Drop { slot, text } => {
34
+ format!(r#"{{"op":"drop","slot":{},"text":"{}"}}"#, slot, hex(text))
35
+ }
36
+ })
37
+ .collect::<Vec<_>>()
38
+ .join(",")
39
+ }
40
+ fn main() {
41
+ let mut args = std::env::args().skip(1);
42
+ let kind = args.next().unwrap();
43
+ let w: Vec<u32> = args.map(|v| v.parse().unwrap()).collect();
44
+ match kind.as_str() {
45
+ "route" => {
46
+ assert_eq!(w.len(), 4);
47
+ let out = route_client(RoutingInput {
48
+ intermission: w[0],
49
+ team: w[1],
50
+ follow: w[2],
51
+ spectator: w[3],
52
+ slot: 2,
53
+ command_time: 12345,
54
+ })
55
+ .unwrap();
56
+ println!(
57
+ r#"{{"events":[{}],"proceed":{}}}"#,
58
+ events(&out.effects),
59
+ out.route == ClientRoute::Continue
60
+ );
61
+ }
62
+ _ => panic!("unknown policy"),
63
+ }
64
+ }
crates/urt-gameplay/src/client_processing.rs ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //! Recovered pre-movement client policies with explicit captured callback effects.
2
+ use crate::native_policy::Error;
3
+ #[derive(Clone, Debug, PartialEq, Eq)]
4
+ pub enum ProcessingEffect {
5
+ Intermission {
6
+ slot: i32,
7
+ },
8
+ Spectator {
9
+ slot: i32,
10
+ command_time: u32,
11
+ },
12
+ Team {
13
+ slot: i32,
14
+ text: Vec<u8>,
15
+ force: u32,
16
+ },
17
+ Send {
18
+ slot: i32,
19
+ text: Vec<u8>,
20
+ },
21
+ Drop {
22
+ slot: i32,
23
+ text: Vec<u8>,
24
+ },
25
+ }
26
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
27
+ pub enum ClientRoute {
28
+ Continue,
29
+ Intermission,
30
+ Spectator,
31
+ Return,
32
+ }
33
+ #[derive(Clone, Debug, PartialEq, Eq)]
34
+ pub struct RoutingOutcome {
35
+ pub route: ClientRoute,
36
+ pub effects: Vec<ProcessingEffect>,
37
+ }
38
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
39
+ pub struct RoutingInput {
40
+ pub intermission: u32,
41
+ pub team: u32,
42
+ pub follow: u32,
43
+ pub spectator: u32,
44
+ pub slot: i32,
45
+ pub command_time: u32,
46
+ }
47
+ /// Original154210 routing, stopping before command rewriting or at early return.
48
+ pub fn route_client(input: RoutingInput) -> Result<RoutingOutcome, Error> {
49
+ if !(0..64).contains(&input.slot) {
50
+ return Err(Error::InvalidIndex);
51
+ }
52
+ let (route, effects) = if input.intermission != 0 {
53
+ (
54
+ ClientRoute::Intermission,
55
+ vec![ProcessingEffect::Intermission { slot: input.slot }],
56
+ )
57
+ } else if input.team == 3 || input.follow != 0 {
58
+ if input.spectator == 3 {
59
+ (ClientRoute::Return, vec![])
60
+ } else {
61
+ (
62
+ ClientRoute::Spectator,
63
+ vec![ProcessingEffect::Spectator {
64
+ slot: input.slot,
65
+ command_time: input.command_time,
66
+ }],
67
+ )
68
+ }
69
+ } else {
70
+ (ClientRoute::Continue, vec![])
71
+ };
72
+ Ok(RoutingOutcome { route, effects })
73
+ }
74
+ #[cfg(test)]
75
+ mod routing_tests {
76
+ use super::*;
77
+ fn input() -> RoutingInput {
78
+ RoutingInput {
79
+ intermission: 0,
80
+ team: 0,
81
+ follow: 0,
82
+ spectator: 0,
83
+ slot: 2,
84
+ command_time: 12345,
85
+ }
86
+ }
87
+ #[test]
88
+ fn intermission_precedes_spectator_state() {
89
+ let p = route_client(RoutingInput {
90
+ intermission: 1,
91
+ team: 3,
92
+ follow: 1,
93
+ spectator: 3,
94
+ ..input()
95
+ })
96
+ .unwrap();
97
+ assert_eq!(p.route, ClientRoute::Intermission);
98
+ assert_eq!(p.effects, [ProcessingEffect::Intermission { slot: 2 }]);
99
+ }
100
+ #[test]
101
+ fn spectator_word_only_suppresses_with_outer_gate() {
102
+ assert_eq!(
103
+ route_client(RoutingInput {
104
+ spectator: 3,
105
+ ..input()
106
+ })
107
+ .unwrap()
108
+ .route,
109
+ ClientRoute::Continue
110
+ );
111
+ let p = route_client(RoutingInput {
112
+ team: 3,
113
+ spectator: 3,
114
+ ..input()
115
+ })
116
+ .unwrap();
117
+ assert_eq!(p.route, ClientRoute::Return);
118
+ assert!(p.effects.is_empty());
119
+ assert_eq!(
120
+ route_client(RoutingInput {
121
+ follow: 1,
122
+ ..input()
123
+ })
124
+ .unwrap()
125
+ .route,
126
+ ClientRoute::Spectator
127
+ );
128
+ }
129
+ }
crates/urt-gameplay/src/lib.rs CHANGED
@@ -189,3 +189,5 @@ pub mod filter_loading;
189
  pub mod filter_commands;
190
 
191
  pub mod client_timer;
 
 
 
189
  pub mod filter_commands;
190
 
191
  pub mod client_timer;
192
+
193
+ pub mod client_processing;
docs/FINDINGS.md CHANGED
@@ -1881,3 +1881,5 @@ RE-972: Ninety-six original154089-prefix cases recover connection-word gating, o
1881
  RE-973: Rust matches96 original caller-prefix cases, preserving connection-word gating, pre-clamp timestamp copies, signed wrapping bounds, the nonpositive-delta exception,200ms cap and post-delta8ms rounding. Unit tests cover unchanged disconnected state, retained pre-round delta, negative exception deltas and sequential bounds at signed wrap. The remaining caller body and actual timer dispatch are explicitly unrecovered. [Evidence](research/RUST_CLIENT_TIMER_PREPARE.md).
1882
 
1883
  RE-974: Sixteen source154210-prefix paths recover intermission precedence, team3/follow gates and spectator-word3 callback suppression. Normal paths stop at154256; routed paths stop at the original early return155716. Callback151133/149671 effects remain captured, with their client/command inputs recorded. Later command and movement processing are outside this first contract. [Evidence](research/QVM_CLIENT_ROUTING.md).
 
 
 
1881
  RE-973: Rust matches96 original caller-prefix cases, preserving connection-word gating, pre-clamp timestamp copies, signed wrapping bounds, the nonpositive-delta exception,200ms cap and post-delta8ms rounding. Unit tests cover unchanged disconnected state, retained pre-round delta, negative exception deltas and sequential bounds at signed wrap. The remaining caller body and actual timer dispatch are explicitly unrecovered. [Evidence](research/RUST_CLIENT_TIMER_PREPARE.md).
1882
 
1883
  RE-974: Sixteen source154210-prefix paths recover intermission precedence, team3/follow gates and spectator-word3 callback suppression. Normal paths stop at154256; routed paths stop at the original early return155716. Callback151133/149671 effects remain captured, with their client/command inputs recorded. Later command and movement processing are outside this first contract. [Evidence](research/QVM_CLIENT_ROUTING.md).
1884
+
1885
+ RE-975: Rust matches16 source routing cases, preserving intermission precedence and spectator callback suppression. Typed effects carry source slot/command-time inputs and keep actual callback bodies outside this contract. Unit tests cover outer-gate precedence and spectator-word suppression without conflating normal clients with routed ones. [Evidence](research/RUST_CLIENT_ROUTING.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1935,3 +1935,5 @@ RE-954–963 completed add/remove wrappers, resolved duration export, full-capac
1935
  RE-964–973 completed timer deadline/phase/recovery contracts, whole-body composition and caller timing preparation. RE-974 tracks remaining caller paths, damage feedback and native/runtime integration. [Batch and next work](research/QVM_CLIENT_TIMER_BATCH.md).
1936
 
1937
  - [x] RE-974 Original client intermission and spectator routing ([evidence](research/QVM_CLIENT_ROUTING.md)).
 
 
 
1935
  RE-964–973 completed timer deadline/phase/recovery contracts, whole-body composition and caller timing preparation. RE-974 tracks remaining caller paths, damage feedback and native/runtime integration. [Batch and next work](research/QVM_CLIENT_TIMER_BATCH.md).
1936
 
1937
  - [x] RE-974 Original client intermission and spectator routing ([evidence](research/QVM_CLIENT_ROUTING.md)).
1938
+
1939
+ - [x] RE-975 Rust early client routing with captured callbacks ([evidence](research/RUST_CLIENT_ROUTING.md)).
docs/RESEARCH_LEDGER.md CHANGED
@@ -2569,3 +2569,7 @@ Batch RE-964–973 boundary: the single `cargo xtask test-full --since c1e91e2e`
2569
  ## 2026-10-28 — RE-974 Original client intermission and spectator routing
2570
 
2571
  Sixteen source154210-prefix paths recover intermission precedence, team3/follow gates and spectator-word3 callback suppression. Normal paths stop at154256; routed paths stop at the original early return155716. Callback151133/149671 effects remain captured, with their client/command inputs recorded. Later command and movement processing are outside this first contract. Focused `cargo xtask test-group qvm-client-routing` passes with deterministic report regeneration, relevant regressions and storage/documentation/whitespace checks. Full suite deferred to the tenth-commit boundary. Original assets unchanged; native IO effects remain captured.
 
 
 
 
 
2569
  ## 2026-10-28 — RE-974 Original client intermission and spectator routing
2570
 
2571
  Sixteen source154210-prefix paths recover intermission precedence, team3/follow gates and spectator-word3 callback suppression. Normal paths stop at154256; routed paths stop at the original early return155716. Callback151133/149671 effects remain captured, with their client/command inputs recorded. Later command and movement processing are outside this first contract. Focused `cargo xtask test-group qvm-client-routing` passes with deterministic report regeneration, relevant regressions and storage/documentation/whitespace checks. Full suite deferred to the tenth-commit boundary. Original assets unchanged; native IO effects remain captured.
2572
+
2573
+ ## 2026-10-28 — RE-975 Rust early client routing with captured callbacks
2574
+
2575
+ Rust matches16 source routing cases, preserving intermission precedence and spectator callback suppression. Typed effects carry source slot/command-time inputs and keep actual callback bodies outside this contract. Unit tests cover outer-gate precedence and spectator-word suppression without conflating normal clients with routed ones. Focused `cargo xtask test-group rust-client-routing` passes with deterministic report regeneration, relevant regressions and storage/documentation/whitespace checks. Full suite deferred to the tenth-commit boundary. Original assets unchanged; native IO effects remain captured.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -1972,3 +1972,5 @@ RE-964–973 recover the complete bounded timer body and its caller timing prefi
1972
  - [ ] RE-974 Recover the remaining timer caller, damage feedback, initialization/native ownership and full Rust/Bevy/Solari acceptance ([next work](research/QVM_CLIENT_TIMER_BATCH.md)).
1973
 
1974
  - [x] RE-974 Original client intermission and spectator routing ([evidence](research/QVM_CLIENT_ROUTING.md)).
 
 
 
1972
  - [ ] RE-974 Recover the remaining timer caller, damage feedback, initialization/native ownership and full Rust/Bevy/Solari acceptance ([next work](research/QVM_CLIENT_TIMER_BATCH.md)).
1973
 
1974
  - [x] RE-974 Original client intermission and spectator routing ([evidence](research/QVM_CLIENT_ROUTING.md)).
1975
+
1976
+ - [x] RE-975 Rust early client routing with captured callbacks ([evidence](research/RUST_CLIENT_ROUTING.md)).
docs/research/README.md CHANGED
@@ -1507,3 +1507,5 @@ The extension collector adds five focused tests for native tail calls, command-r
1507
  - [RE-964–973 client timer batch and RE-974 next work](QVM_CLIENT_TIMER_BATCH.md).
1508
 
1509
  - [RE-974: Original client intermission and spectator routing](QVM_CLIENT_ROUTING.md).
 
 
 
1507
  - [RE-964–973 client timer batch and RE-974 next work](QVM_CLIENT_TIMER_BATCH.md).
1508
 
1509
  - [RE-974: Original client intermission and spectator routing](QVM_CLIENT_ROUTING.md).
1510
+
1511
+ - [RE-975: Rust early client routing with captured callbacks](RUST_CLIENT_ROUTING.md).
docs/research/RUST_CLIENT_ROUTING.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Rust early client routing with captured callbacks
2
+
3
+ RE-975. Focused validation: `cargo xtask test-group rust-client-routing`. [Machine report](rust-client-routing.json).
4
+
5
+ Rust matches16 source routing cases, preserving intermission precedence and spectator callback suppression. Typed effects carry source slot/command-time inputs and keep actual callback bodies outside this contract. Unit tests cover outer-gate precedence and spectator-word suppression without conflating normal clients with routed ones.
docs/research/rust-client-routing.json ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "limits": "Finite source/Rust contract parity; native time/file/network replies remain supplied or captured.",
3
+ "probes": [
4
+ {
5
+ "rust": {
6
+ "events": [],
7
+ "proceed": true
8
+ },
9
+ "source": {
10
+ "events": [],
11
+ "proceed": true
12
+ }
13
+ },
14
+ {
15
+ "rust": {
16
+ "events": [],
17
+ "proceed": true
18
+ },
19
+ "source": {
20
+ "events": [],
21
+ "proceed": true
22
+ }
23
+ },
24
+ {
25
+ "rust": {
26
+ "events": [
27
+ {
28
+ "command_time": 12345,
29
+ "op": "spectator",
30
+ "slot": 2
31
+ }
32
+ ],
33
+ "proceed": false
34
+ },
35
+ "source": {
36
+ "events": [
37
+ {
38
+ "command_time": 12345,
39
+ "op": "spectator",
40
+ "slot": 2
41
+ }
42
+ ],
43
+ "proceed": false
44
+ }
45
+ },
46
+ {
47
+ "rust": {
48
+ "events": [],
49
+ "proceed": false
50
+ },
51
+ "source": {
52
+ "events": [],
53
+ "proceed": false
54
+ }
55
+ },
56
+ {
57
+ "rust": {
58
+ "events": [
59
+ {
60
+ "command_time": 12345,
61
+ "op": "spectator",
62
+ "slot": 2
63
+ }
64
+ ],
65
+ "proceed": false
66
+ },
67
+ "source": {
68
+ "events": [
69
+ {
70
+ "command_time": 12345,
71
+ "op": "spectator",
72
+ "slot": 2
73
+ }
74
+ ],
75
+ "proceed": false
76
+ }
77
+ },
78
+ {
79
+ "rust": {
80
+ "events": [],
81
+ "proceed": false
82
+ },
83
+ "source": {
84
+ "events": [],
85
+ "proceed": false
86
+ }
87
+ },
88
+ {
89
+ "rust": {
90
+ "events": [
91
+ {
92
+ "command_time": 12345,
93
+ "op": "spectator",
94
+ "slot": 2
95
+ }
96
+ ],
97
+ "proceed": false
98
+ },
99
+ "source": {
100
+ "events": [
101
+ {
102
+ "command_time": 12345,
103
+ "op": "spectator",
104
+ "slot": 2
105
+ }
106
+ ],
107
+ "proceed": false
108
+ }
109
+ },
110
+ {
111
+ "rust": {
112
+ "events": [],
113
+ "proceed": false
114
+ },
115
+ "source": {
116
+ "events": [],
117
+ "proceed": false
118
+ }
119
+ },
120
+ {
121
+ "rust": {
122
+ "events": [
123
+ {
124
+ "op": "intermission",
125
+ "slot": 2
126
+ }
127
+ ],
128
+ "proceed": false
129
+ },
130
+ "source": {
131
+ "events": [
132
+ {
133
+ "op": "intermission",
134
+ "slot": 2
135
+ }
136
+ ],
137
+ "proceed": false
138
+ }
139
+ },
140
+ {
141
+ "rust": {
142
+ "events": [
143
+ {
144
+ "op": "intermission",
145
+ "slot": 2
146
+ }
147
+ ],
148
+ "proceed": false
149
+ },
150
+ "source": {
151
+ "events": [
152
+ {
153
+ "op": "intermission",
154
+ "slot": 2
155
+ }
156
+ ],
157
+ "proceed": false
158
+ }
159
+ },
160
+ {
161
+ "rust": {
162
+ "events": [
163
+ {
164
+ "op": "intermission",
165
+ "slot": 2
166
+ }
167
+ ],
168
+ "proceed": false
169
+ },
170
+ "source": {
171
+ "events": [
172
+ {
173
+ "op": "intermission",
174
+ "slot": 2
175
+ }
176
+ ],
177
+ "proceed": false
178
+ }
179
+ },
180
+ {
181
+ "rust": {
182
+ "events": [
183
+ {
184
+ "op": "intermission",
185
+ "slot": 2
186
+ }
187
+ ],
188
+ "proceed": false
189
+ },
190
+ "source": {
191
+ "events": [
192
+ {
193
+ "op": "intermission",
194
+ "slot": 2
195
+ }
196
+ ],
197
+ "proceed": false
198
+ }
199
+ },
200
+ {
201
+ "rust": {
202
+ "events": [
203
+ {
204
+ "op": "intermission",
205
+ "slot": 2
206
+ }
207
+ ],
208
+ "proceed": false
209
+ },
210
+ "source": {
211
+ "events": [
212
+ {
213
+ "op": "intermission",
214
+ "slot": 2
215
+ }
216
+ ],
217
+ "proceed": false
218
+ }
219
+ },
220
+ {
221
+ "rust": {
222
+ "events": [
223
+ {
224
+ "op": "intermission",
225
+ "slot": 2
226
+ }
227
+ ],
228
+ "proceed": false
229
+ },
230
+ "source": {
231
+ "events": [
232
+ {
233
+ "op": "intermission",
234
+ "slot": 2
235
+ }
236
+ ],
237
+ "proceed": false
238
+ }
239
+ },
240
+ {
241
+ "rust": {
242
+ "events": [
243
+ {
244
+ "op": "intermission",
245
+ "slot": 2
246
+ }
247
+ ],
248
+ "proceed": false
249
+ },
250
+ "source": {
251
+ "events": [
252
+ {
253
+ "op": "intermission",
254
+ "slot": 2
255
+ }
256
+ ],
257
+ "proceed": false
258
+ }
259
+ },
260
+ {
261
+ "rust": {
262
+ "events": [
263
+ {
264
+ "op": "intermission",
265
+ "slot": 2
266
+ }
267
+ ],
268
+ "proceed": false
269
+ },
270
+ "source": {
271
+ "events": [
272
+ {
273
+ "op": "intermission",
274
+ "slot": 2
275
+ }
276
+ ],
277
+ "proceed": false
278
+ }
279
+ }
280
+ ],
281
+ "source_report": "docs/research/qvm-client-routing.json",
282
+ "source_report_sha256": "dafa28fafa50f72fa77e876c28e979ac3ad0084dc78bcd1103dd598589449b16"
283
+ }
docs/research/rust-health-impulse.json CHANGED
@@ -2985,7 +2985,7 @@
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
- "rust_unit_tests": 230,
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": 232,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
scripts/analyze_rust_client_routing.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Compare recovered Rust policy with original QVM fixtures."""
2
+ import hashlib,json,os,subprocess
3
+ from pathlib import Path
4
+ from analyze_qvm import write_json
5
+ from rust_lifecycle_support import result
6
+
7
+ def main():
8
+ subprocess.run(['cargo','build','--offline','--package','urt-gameplay','--bin','urt-client-processing'],check=True,capture_output=True)
9
+ binary=Path(os.environ.get('CARGO_TARGET_DIR','target'))/'debug'/'urt-client-processing'
10
+ path=Path('docs/research/qvm-client-routing.json');data=path.read_bytes();probes=[]
11
+ for p in json.loads(data)['probes']:
12
+ probes.append(dict(source={k:p[k] for k in ['events', 'proceed']},rust=result(binary,'route',encode(p['input']))))
13
+ write_json(Path('docs/research/rust-client-routing.json'),dict(source_report=str(path),source_report_sha256=hashlib.sha256(data).hexdigest(),probes=probes,limits='Finite source/Rust contract parity; native time/file/network replies remain supplied or captured.'))
14
+
15
+ def encode(a):return [a[k] for k in ['intermission','team','follow','spectator']]
16
+ if __name__=='__main__':main()
scripts/test_rust_client_routing.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib,json,unittest
2
+ from pathlib import Path
3
+ class PortTests(unittest.TestCase):
4
+ def test_original_parity(self):
5
+ r=json.loads(Path('docs/research/rust-client-routing.json').read_text());self.assertGreaterEqual(len(r['probes']),12)
6
+ for p in r['probes']:self.assertEqual(p['source'],p['rust'])
7
+ def test_identity(self):
8
+ r=json.loads(Path('docs/research/rust-client-routing.json').read_text());self.assertEqual(hashlib.sha256(Path(r['source_report']).read_bytes()).hexdigest(),r['source_report_sha256'])
9
+ if __name__=='__main__':unittest.main()
xtask/checks.tsv CHANGED
@@ -3542,3 +3542,27 @@ report scripts/analyze_qvm_client_routing.py docs/research/qvm-client-routing.js
3542
  edge docs/research/qvm-client-routing.json scripts/test_qvm_client_routing.py
3543
  group qvm-client-routing scripts/analyze_qvm_client_routing.py
3544
  edge scripts/analyze_qvm_client_routing.py scripts/analyze_qvm_resumer_audit.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3542
  edge docs/research/qvm-client-routing.json scripts/test_qvm_client_routing.py
3543
  group qvm-client-routing scripts/analyze_qvm_client_routing.py
3544
  edge scripts/analyze_qvm_client_routing.py scripts/analyze_qvm_resumer_audit.py
3545
+ report scripts/analyze_rust_client_routing.py docs/research/rust-client-routing.json
3546
+ edge docs/research/rust-client-routing.json scripts/test_rust_client_routing.py
3547
+ group rust-client-routing scripts/analyze_rust_client_routing.py
3548
+ edge scripts/analyze_rust_client_routing.py scripts/analyze_qvm_resumer_audit.py
3549
+ edge Cargo.toml scripts/analyze_rust_client_routing.py
3550
+ edge Cargo.lock scripts/analyze_rust_client_routing.py
3551
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_client_routing.py
3552
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_client_routing.py
3553
+ edge crates/urt-gameplay/src/filter_loading.rs scripts/analyze_rust_client_routing.py
3554
+ edge crates/urt-gameplay/src/filter_lifecycle.rs scripts/analyze_rust_client_routing.py
3555
+ edge crates/urt-gameplay/src/native_policy.rs scripts/analyze_rust_client_routing.py
3556
+ edge crates/urt-gameplay/src/bin/urt-filter-loading.rs scripts/analyze_rust_client_routing.py
3557
+ edge crates/urt-gameplay/src/qvm_format.rs scripts/analyze_rust_client_routing.py
3558
+ edge docs/research/qvm-client-routing.json scripts/analyze_rust_client_routing.py
3559
+ group rust-client-routing scripts/analyze_rust_health_impulse.py
3560
+ group gameplay scripts/analyze_rust_client_routing.py
3561
+ edge crates/urt-gameplay/src/filter_commands.rs scripts/analyze_rust_client_routing.py
3562
+ edge crates/urt-gameplay/src/bin/urt-filter-commands.rs scripts/analyze_rust_client_routing.py
3563
+ edge crates/urt-gameplay/src/client_timer.rs scripts/analyze_rust_client_routing.py
3564
+ edge crates/urt-gameplay/src/bin/urt-client-timer.rs scripts/analyze_rust_client_routing.py
3565
+ edge crates/urt-gameplay/src/client_processing.rs scripts/analyze_rust_client_routing.py
3566
+ edge crates/urt-gameplay/src/bin/urt-client-processing.rs scripts/analyze_rust_client_routing.py
3567
+ edge crates/urt-gameplay/src/client_processing.rs scripts/analyze_rust_health_impulse.py
3568
+ edge crates/urt-gameplay/src/bin/urt-client-processing.rs scripts/analyze_rust_health_impulse.py