gameplay: port method counter updates
Browse files- crates/urt-gameplay/src/bin/urt-lifecycle.rs +62 -0
- crates/urt-gameplay/src/lib.rs +1 -0
- crates/urt-gameplay/src/method_stats.rs +167 -0
- docs/ASSET_PIPELINE_TODO.md +3 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +3 -0
- docs/RESEARCH_LEDGER.md +5 -0
- docs/REVERSE_ENGINEERING_TODO.md +3 -0
- docs/research/README.md +3 -0
- docs/research/RUST_METHOD_STATS.md +7 -0
- docs/research/rust-health-impulse.json +1 -1
- docs/research/rust-method-stats.json +0 -0
- scripts/analyze_rust_method_stats.py +14 -0
- scripts/test_rust_method_stats.py +17 -0
- xtask/checks.tsv +10 -0
crates/urt-gameplay/src/bin/urt-lifecycle.rs
CHANGED
|
@@ -105,6 +105,68 @@ fn run() -> Result<(), String> {
|
|
| 105 |
refresh
|
| 106 |
);
|
| 107 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
_ => return Err("unsupported contract or arguments".into()),
|
| 109 |
}
|
| 110 |
Ok(())
|
|
|
|
| 105 |
refresh
|
| 106 |
);
|
| 107 |
}
|
| 108 |
+
"classify" if w.len() == 1 => {
|
| 109 |
+
println!(
|
| 110 |
+
"{{\"slot\":{}}}",
|
| 111 |
+
urt_gameplay::method_stats::slot(w[0] as i32)
|
| 112 |
+
.map(i32::from)
|
| 113 |
+
.unwrap_or(-1)
|
| 114 |
+
);
|
| 115 |
+
}
|
| 116 |
+
"stats" if w.len() == 4 && w[1] <= 1 && w[3] <= 1 => {
|
| 117 |
+
use std::collections::BTreeMap;
|
| 118 |
+
use urt_gameplay::method_stats::{Side, apply, plan};
|
| 119 |
+
let p = plan(w[0] as i32, w[1] != 0, w[3] != 0);
|
| 120 |
+
let mut actor: BTreeMap<u32, u32> = p
|
| 121 |
+
.increments
|
| 122 |
+
.iter()
|
| 123 |
+
.filter(|(s, _)| *s == Side::Actor)
|
| 124 |
+
.map(|(_, o)| (*o, w[2]))
|
| 125 |
+
.collect();
|
| 126 |
+
let mut victim: BTreeMap<u32, u32> = p
|
| 127 |
+
.increments
|
| 128 |
+
.iter()
|
| 129 |
+
.filter(|(s, _)| *s == Side::Victim)
|
| 130 |
+
.map(|(_, o)| (*o, w[2]))
|
| 131 |
+
.collect();
|
| 132 |
+
let p = apply(w[0] as i32, w[1] != 0, w[3] != 0, &mut actor, &mut victim)
|
| 133 |
+
.map_err(|e| format!("unsupported input: {e:?}"))?;
|
| 134 |
+
let mut increments = p.increments;
|
| 135 |
+
increments.sort();
|
| 136 |
+
let changes: Vec<_> = increments
|
| 137 |
+
.iter()
|
| 138 |
+
.map(|(side, o)| {
|
| 139 |
+
format!(
|
| 140 |
+
"{{\"client\":\"{}\",\"offset\":{},\"before\":{},\"after\":{}}}",
|
| 141 |
+
if *side == Side::Actor {
|
| 142 |
+
"actor"
|
| 143 |
+
} else {
|
| 144 |
+
"victim"
|
| 145 |
+
},
|
| 146 |
+
o,
|
| 147 |
+
w[2],
|
| 148 |
+
if *side == Side::Actor {
|
| 149 |
+
actor[o]
|
| 150 |
+
} else {
|
| 151 |
+
victim[o]
|
| 152 |
+
}
|
| 153 |
+
)
|
| 154 |
+
})
|
| 155 |
+
.collect();
|
| 156 |
+
let mut calls = vec![];
|
| 157 |
+
if p.aggregate_pair {
|
| 158 |
+
calls.push(279720);
|
| 159 |
+
}
|
| 160 |
+
if p.report {
|
| 161 |
+
calls.extend([279586, 279586, 285227, 283622]);
|
| 162 |
+
}
|
| 163 |
+
let calls: Vec<_> = calls.iter().map(|c| c.to_string()).collect();
|
| 164 |
+
println!(
|
| 165 |
+
"{{\"changes\":[{}],\"calls\":[{}]}}",
|
| 166 |
+
changes.join(","),
|
| 167 |
+
calls.join(",")
|
| 168 |
+
);
|
| 169 |
+
}
|
| 170 |
_ => return Err("unsupported contract or arguments".into()),
|
| 171 |
}
|
| 172 |
Ok(())
|
crates/urt-gameplay/src/lib.rs
CHANGED
|
@@ -146,6 +146,7 @@ mod tests {
|
|
| 146 |
}
|
| 147 |
|
| 148 |
pub mod intercept_animation;
|
|
|
|
| 149 |
pub mod mode10;
|
| 150 |
pub mod protected;
|
| 151 |
pub mod score;
|
|
|
|
| 146 |
}
|
| 147 |
|
| 148 |
pub mod intercept_animation;
|
| 149 |
+
pub mod method_stats;
|
| 150 |
pub mod mode10;
|
| 151 |
pub mod protected;
|
| 152 |
pub mod score;
|
crates/urt-gameplay/src/method_stats.rs
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! Numeric278733/278834 counters; aggregation/reporting remains caller work.
|
| 2 |
+
use std::collections::BTreeMap;
|
| 3 |
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
| 4 |
+
pub enum Side {
|
| 5 |
+
Victim,
|
| 6 |
+
Actor,
|
| 7 |
+
}
|
| 8 |
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
| 9 |
+
pub struct Plan {
|
| 10 |
+
pub increments: Vec<(Side, u32)>,
|
| 11 |
+
pub aggregate_pair: bool,
|
| 12 |
+
pub report: bool,
|
| 13 |
+
}
|
| 14 |
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
| 15 |
+
pub enum StatsError {
|
| 16 |
+
MissingCounter,
|
| 17 |
+
}
|
| 18 |
+
pub fn slot(method: i32) -> Option<u8> {
|
| 19 |
+
Some(match method {
|
| 20 |
+
14 => 9,
|
| 21 |
+
15 => 8,
|
| 22 |
+
16 => 11,
|
| 23 |
+
17 => 5,
|
| 24 |
+
18 => 4,
|
| 25 |
+
19 => 2,
|
| 26 |
+
20 => 0,
|
| 27 |
+
21 => 7,
|
| 28 |
+
28 => 6,
|
| 29 |
+
30 => 1,
|
| 30 |
+
36 => 10,
|
| 31 |
+
38 => 3,
|
| 32 |
+
39 => 12,
|
| 33 |
+
40 => 13,
|
| 34 |
+
41 => 14,
|
| 35 |
+
42 => 15,
|
| 36 |
+
43 => 16,
|
| 37 |
+
44 => 17,
|
| 38 |
+
45 => 18,
|
| 39 |
+
_ => return None,
|
| 40 |
+
})
|
| 41 |
+
}
|
| 42 |
+
/// All participants are existing clients with valid source IDs. Self aliases
|
| 43 |
+
/// resolve to the actor's storage; an absent actor exits before any requests.
|
| 44 |
+
pub fn plan(method: i32, self_hit: bool, actor_absent: bool) -> Plan {
|
| 45 |
+
use Side::{Actor, Victim};
|
| 46 |
+
let mut result = Plan {
|
| 47 |
+
increments: vec![],
|
| 48 |
+
aggregate_pair: false,
|
| 49 |
+
report: !actor_absent,
|
| 50 |
+
};
|
| 51 |
+
if actor_absent {
|
| 52 |
+
return result;
|
| 53 |
+
}
|
| 54 |
+
let victim = if self_hit { Actor } else { Victim };
|
| 55 |
+
if let Some(slot) = slot(method) {
|
| 56 |
+
result.increments = vec![
|
| 57 |
+
(Actor, 916 + 76 * u32::from(slot)),
|
| 58 |
+
(victim, 920 + 76 * u32::from(slot)),
|
| 59 |
+
];
|
| 60 |
+
result.aggregate_pair = true;
|
| 61 |
+
return result;
|
| 62 |
+
}
|
| 63 |
+
let single = match method {
|
| 64 |
+
0..=5 | 8 | 9 => Some(2512),
|
| 65 |
+
6 => Some(2488),
|
| 66 |
+
7 => Some(2492),
|
| 67 |
+
31 => Some(2504),
|
| 68 |
+
32 | 33 | 35 => Some(2508),
|
| 69 |
+
47 => Some(2496),
|
| 70 |
+
22 if self_hit => Some(2460),
|
| 71 |
+
25 if self_hit => Some(2452),
|
| 72 |
+
_ => None,
|
| 73 |
+
};
|
| 74 |
+
if let Some(offset) = single {
|
| 75 |
+
result.increments.push((victim, offset));
|
| 76 |
+
return result;
|
| 77 |
+
}
|
| 78 |
+
let pair = match method {
|
| 79 |
+
12 => Some((2396, 2468)),
|
| 80 |
+
13 => Some((2400, 2472)),
|
| 81 |
+
22 => Some((2384, 2456)),
|
| 82 |
+
23 => Some((2408, 2480)),
|
| 83 |
+
24 => Some((2404, 2476)),
|
| 84 |
+
25 => Some((2376, 2448)),
|
| 85 |
+
34 => Some((2428, 2500)),
|
| 86 |
+
37 => Some((2392, 2464)),
|
| 87 |
+
48 => Some((2412, 2484)),
|
| 88 |
+
_ => None,
|
| 89 |
+
};
|
| 90 |
+
if let Some((a, v)) = pair {
|
| 91 |
+
result.increments = vec![(Actor, a), (victim, v)];
|
| 92 |
+
result.aggregate_pair = method != 34;
|
| 93 |
+
}
|
| 94 |
+
result
|
| 95 |
+
}
|
| 96 |
+
/// Validate storage before mutation, then apply source wrapping increments.
|
| 97 |
+
/// The returned plan describes reporting intent without executing callbacks.
|
| 98 |
+
pub fn apply(
|
| 99 |
+
method: i32,
|
| 100 |
+
self_hit: bool,
|
| 101 |
+
actor_absent: bool,
|
| 102 |
+
actor: &mut BTreeMap<u32, u32>,
|
| 103 |
+
victim: &mut BTreeMap<u32, u32>,
|
| 104 |
+
) -> Result<Plan, StatsError> {
|
| 105 |
+
let p = plan(method, self_hit, actor_absent);
|
| 106 |
+
for (side, offset) in &p.increments {
|
| 107 |
+
if !(if *side == Side::Actor {
|
| 108 |
+
&*actor
|
| 109 |
+
} else {
|
| 110 |
+
&*victim
|
| 111 |
+
})
|
| 112 |
+
.contains_key(offset)
|
| 113 |
+
{
|
| 114 |
+
return Err(StatsError::MissingCounter);
|
| 115 |
+
}
|
| 116 |
+
}
|
| 117 |
+
for (side, offset) in &p.increments {
|
| 118 |
+
let counters = if *side == Side::Actor {
|
| 119 |
+
&mut *actor
|
| 120 |
+
} else {
|
| 121 |
+
&mut *victim
|
| 122 |
+
};
|
| 123 |
+
let v = counters.get_mut(offset).unwrap();
|
| 124 |
+
*v = v.wrapping_add(1);
|
| 125 |
+
}
|
| 126 |
+
Ok(p)
|
| 127 |
+
}
|
| 128 |
+
#[cfg(test)]
|
| 129 |
+
mod tests {
|
| 130 |
+
use super::*;
|
| 131 |
+
#[test]
|
| 132 |
+
fn all_slots_and_invalid_signed_extremes() {
|
| 133 |
+
let slots: std::collections::BTreeSet<_> = (0..50).filter_map(slot).collect();
|
| 134 |
+
assert_eq!(slots, (0..19).collect());
|
| 135 |
+
assert_eq!(slot(i32::MIN), None);
|
| 136 |
+
assert_eq!(slot(i32::MAX), None);
|
| 137 |
+
}
|
| 138 |
+
#[test]
|
| 139 |
+
fn self_alias_and_fallback_exceptions() {
|
| 140 |
+
assert_eq!(plan(22, true, false).increments, vec![(Side::Actor, 2460)]);
|
| 141 |
+
assert_eq!(plan(25, true, false).increments, vec![(Side::Actor, 2452)]);
|
| 142 |
+
assert!(!plan(34, false, false).aggregate_pair);
|
| 143 |
+
assert!(plan(20, false, false).aggregate_pair);
|
| 144 |
+
}
|
| 145 |
+
#[test]
|
| 146 |
+
fn apply_wraps_and_keeps_other_counters() {
|
| 147 |
+
let mut actor = BTreeMap::from([(916, u32::MAX), (999, 17)]);
|
| 148 |
+
let mut victim = BTreeMap::from([(920, 3)]);
|
| 149 |
+
apply(20, false, false, &mut actor, &mut victim).unwrap();
|
| 150 |
+
assert_eq!(actor, BTreeMap::from([(916, 0), (999, 17)]));
|
| 151 |
+
assert_eq!(victim[&920], 4);
|
| 152 |
+
}
|
| 153 |
+
#[test]
|
| 154 |
+
fn absent_actor_and_missing_storage_do_not_partially_mutate() {
|
| 155 |
+
let mut a = BTreeMap::from([(916, 4)]);
|
| 156 |
+
let mut v = BTreeMap::new();
|
| 157 |
+
assert_eq!(
|
| 158 |
+
apply(20, false, false, &mut a, &mut v),
|
| 159 |
+
Err(StatsError::MissingCounter)
|
| 160 |
+
);
|
| 161 |
+
assert_eq!(a[&916], 4);
|
| 162 |
+
let p = apply(20, false, true, &mut a, &mut v).unwrap();
|
| 163 |
+
assert!(!p.report);
|
| 164 |
+
assert!(p.increments.is_empty());
|
| 165 |
+
assert_eq!(a[&916], 4);
|
| 166 |
+
}
|
| 167 |
+
}
|
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -925,3 +925,6 @@ Research progress: RE-753 [report](research/QVM_MODE10_RESOLVED.md); original as
|
|
| 925 |
|
| 926 |
|
| 927 |
Research progress: RE-754 [report](research/RUST_SCORE_UPDATE.md); original assets unchanged.
|
|
|
|
|
|
|
|
|
|
|
|
| 925 |
|
| 926 |
|
| 927 |
Research progress: RE-754 [report](research/RUST_SCORE_UPDATE.md); original assets unchanged.
|
| 928 |
+
|
| 929 |
+
|
| 930 |
+
Research progress: RE-755 [report](research/RUST_METHOD_STATS.md); original assets unchanged.
|
docs/FINDINGS.md
CHANGED
|
@@ -1271,3 +1271,6 @@ RE-725–734 batch: [health, contact and Rust impulse findings](research/QVM_HEA
|
|
| 1271 |
|
| 1272 |
|
| 1273 |
**RE-754 — Rust wrapping score primitive and ranking request contract:** Rust player/team score updates and ranking-request decisions match67 source fixtures, with explicit active team-index rejection. [Evidence](research/RUST_SCORE_UPDATE.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1271 |
|
| 1272 |
|
| 1273 |
**RE-754 — Rust wrapping score primitive and ranking request contract:** Rust player/team score updates and ranking-request decisions match67 source fixtures, with explicit active team-index rejection. [Evidence](research/RUST_SCORE_UPDATE.md).
|
| 1274 |
+
|
| 1275 |
+
|
| 1276 |
+
**RE-755 — Rust method classification wrapping counters and reporting intent:** Rust classification and method-counter mutation now match53 mapping and205 counter fixtures, including self aliases and wrapping increments. [Evidence](research/RUST_METHOD_STATS.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -1263,3 +1263,6 @@ Research progress: RE-753 [report](research/QVM_MODE10_RESOLVED.md); full native
|
|
| 1263 |
|
| 1264 |
|
| 1265 |
Research progress: RE-754 [report](research/RUST_SCORE_UPDATE.md); full native/platform/game scope remains open.
|
|
|
|
|
|
|
|
|
|
|
|
| 1263 |
|
| 1264 |
|
| 1265 |
Research progress: RE-754 [report](research/RUST_SCORE_UPDATE.md); full native/platform/game scope remains open.
|
| 1266 |
+
|
| 1267 |
+
|
| 1268 |
+
Research progress: RE-755 [report](research/RUST_METHOD_STATS.md); full native/platform/game scope remains open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -1422,3 +1422,8 @@ Additional RE-753 shared-health regression: `cargo xtask test-group health` pass
|
|
| 1422 |
## 2026-10-21 — RE-754 Rust wrapping score primitive and ranking request contract
|
| 1423 |
|
| 1424 |
Rust player/team score updates and ranking-request decisions match67 source fixtures, with explicit active team-index rejection. Focused `cargo xtask test-group gameplay` passes (16 Python tests); selected reports regenerate twice byte-identically, with storage, links and whitespace guards. New report: 33247 bytes. Full-suite validation is deferred to the tenth batch commit. Rust gameplay crate changed; focused gameplay regenerates all five parity contracts and scoped package tests, without requesting workspace/full-suite tests.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1422 |
## 2026-10-21 — RE-754 Rust wrapping score primitive and ranking request contract
|
| 1423 |
|
| 1424 |
Rust player/team score updates and ranking-request decisions match67 source fixtures, with explicit active team-index rejection. Focused `cargo xtask test-group gameplay` passes (16 Python tests); selected reports regenerate twice byte-identically, with storage, links and whitespace guards. New report: 33247 bytes. Full-suite validation is deferred to the tenth batch commit. Rust gameplay crate changed; focused gameplay regenerates all five parity contracts and scoped package tests, without requesting workspace/full-suite tests.
|
| 1425 |
+
|
| 1426 |
+
|
| 1427 |
+
## 2026-10-21 — RE-755 Rust method classification wrapping counters and reporting intent
|
| 1428 |
+
|
| 1429 |
+
Rust classification and method-counter mutation now match53 mapping and205 counter fixtures, including self aliases and wrapping increments. Focused `cargo xtask test-group gameplay` passes (19 Python tests); selected reports regenerate twice byte-identically, with storage, links and whitespace guards. New report: 172131 bytes. Full-suite validation is deferred to the tenth batch commit. Rust gameplay crate changed; focused gameplay regenerates six parity contracts and scoped gameplay tests. No workspace/full-suite run before commit ten.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -1306,3 +1306,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 1306 |
|
| 1307 |
|
| 1308 |
- [x] RE-754 Rust wrapping score primitive and ranking request contract ([report](research/RUST_SCORE_UPDATE.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 1306 |
|
| 1307 |
|
| 1308 |
- [x] RE-754 Rust wrapping score primitive and ranking request contract ([report](research/RUST_SCORE_UPDATE.md)).
|
| 1309 |
+
|
| 1310 |
+
|
| 1311 |
+
- [x] RE-755 Rust method classification wrapping counters and reporting intent ([report](research/RUST_METHOD_STATS.md)).
|
docs/research/README.md
CHANGED
|
@@ -861,3 +861,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 861 |
|
| 862 |
|
| 863 |
[RE-754: Rust wrapping score primitive and ranking request contract](RUST_SCORE_UPDATE.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 861 |
|
| 862 |
|
| 863 |
[RE-754: Rust wrapping score primitive and ranking request contract](RUST_SCORE_UPDATE.md).
|
| 864 |
+
|
| 865 |
+
|
| 866 |
+
[RE-755: Rust method classification wrapping counters and reporting intent](RUST_METHOD_STATS.md).
|
docs/research/RUST_METHOD_STATS.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Rust method classification wrapping counters and reporting intent
|
| 2 |
+
|
| 3 |
+
RE-755. Reproduce with `python3 scripts/analyze_rust_method_stats.py`; validate with `cargo xtask test-group gameplay`. See the [machine report](rust-method-stats.json).
|
| 4 |
+
|
| 5 |
+
Added typed counter ownership, source method mapping, numeric offset plans and transactional storage validation before wrapping mutation. Four Rust unit tests cover complete slot range, fallback self exceptions, untouched counters, overflow, absent actor and missing storage. The adapter checks exact changes and reporting intent against source; aggregate/report callbacks and numeric field-to-runtime data migration remain open.
|
| 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":
|
| 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": 26,
|
| 2989 |
"source_report": "docs/research/qvm-health-impulse.json",
|
| 2990 |
"source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
|
| 2991 |
}
|
docs/research/rust-method-stats.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_rust_method_stats.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Rust method classification and wrapping counter plans against source fixtures."""
|
| 3 |
+
import hashlib,json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from rust_lifecycle_support import binary,result
|
| 6 |
+
from analyze_qvm import write_json
|
| 7 |
+
|
| 8 |
+
def main():
|
| 9 |
+
b=binary();path=Path('docs/research/qvm-method-stat-updates.json');data=path.read_bytes();rows=[]
|
| 10 |
+
for p in json.loads(data)['probes']:
|
| 11 |
+
a=p['input'];args=[a['method'],int(a['self']),a['initial'],int(a.get('actor_absent',False))];e={'changes':p['changes'],'calls':[c['target'] for c in p['requests']]};rows.append({'input':a,'source':e,'rust':result(b,'stats',args)})
|
| 12 |
+
slots=Path('docs/research/qvm-method-stat-slots.json');raw=slots.read_bytes();classification=[{'method':p['method'],'source_slot':p['slot'],'rust':result(b,'classify',[p['method']])} for p in json.loads(raw)['probes']]
|
| 13 |
+
write_json(Path('docs/research/rust-method-stats.json'),{'source_reports':{str(path):hashlib.sha256(data).hexdigest(),str(slots):hashlib.sha256(raw).hexdigest()},'probes':rows,'classification':classification,'limits':'Rust numeric classification matches53 source fixtures; counter mutation/reporting intent matches205 source fixtures including self alias and wrapping. Existing actor/victim clients and valid IDs required. Actual callback/report/aggregation effects and platform/native parity remain open. Missing counter storage rejects before mutation.'})
|
| 14 |
+
if __name__=='__main__':main()
|
scripts/test_rust_method_stats.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib,json,unittest
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_method_stat_updates import expected
|
| 4 |
+
from test_qvm_method_stat_slots import SLOTS
|
| 5 |
+
class RustMethodStatsTests(unittest.TestCase):
|
| 6 |
+
@classmethod
|
| 7 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/rust-method-stats.json').read_text())
|
| 8 |
+
def test_all_source_counter_mutations_and_request_intents(self):
|
| 9 |
+
self.assertEqual(len(self.r['probes']),205)
|
| 10 |
+
for p in self.r['probes']:
|
| 11 |
+
self.assertEqual(p['rust'],p['source'],p['input']);changes,calls=expected(p['input']);self.assertEqual(p['rust'],{'changes':changes,'calls':calls})
|
| 12 |
+
def test_source_classification_and_independent_mapping(self):
|
| 13 |
+
self.assertEqual(len(self.r['classification']),53)
|
| 14 |
+
for p in self.r['classification']:self.assertEqual(p['rust']['slot'],p['source_slot']);self.assertEqual(p['source_slot'],SLOTS.get(p['method'],-1))
|
| 15 |
+
def test_source_identity(self):
|
| 16 |
+
for path,sha in self.r['source_reports'].items():self.assertEqual(hashlib.sha256(Path(path).read_bytes()).hexdigest(),sha)
|
| 17 |
+
if __name__=='__main__':unittest.main()
|
xtask/checks.tsv
CHANGED
|
@@ -1252,3 +1252,13 @@ edge crates/urt-gameplay/src/score.rs scripts/analyze_rust_health_impulse.py
|
|
| 1252 |
group gameplay scripts/analyze_rust_score_update.py
|
| 1253 |
report scripts/analyze_rust_score_update.py docs/research/rust-score-update.json
|
| 1254 |
edge docs/research/rust-score-update.json scripts/test_rust_score_update.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1252 |
group gameplay scripts/analyze_rust_score_update.py
|
| 1253 |
report scripts/analyze_rust_score_update.py docs/research/rust-score-update.json
|
| 1254 |
edge docs/research/rust-score-update.json scripts/test_rust_score_update.py
|
| 1255 |
+
edge Cargo.toml scripts/analyze_rust_method_stats.py
|
| 1256 |
+
edge Cargo.lock scripts/analyze_rust_method_stats.py
|
| 1257 |
+
edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_method_stats.py
|
| 1258 |
+
edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_method_stats.py
|
| 1259 |
+
edge crates/urt-gameplay/src/method_stats.rs scripts/analyze_rust_method_stats.py
|
| 1260 |
+
edge crates/urt-gameplay/src/bin/urt-lifecycle.rs scripts/analyze_rust_method_stats.py
|
| 1261 |
+
edge crates/urt-gameplay/src/method_stats.rs scripts/analyze_rust_health_impulse.py
|
| 1262 |
+
group gameplay scripts/analyze_rust_method_stats.py
|
| 1263 |
+
report scripts/analyze_rust_method_stats.py docs/research/rust-method-stats.json
|
| 1264 |
+
edge docs/research/rust-method-stats.json scripts/test_rust_method_stats.py
|