gameplay: resolve defuse score and counter feedback
Browse files- crates/urt-gameplay/src/bin/urt-effect-feedback.rs +185 -0
- crates/urt-gameplay/src/effect_feedback.rs +232 -0
- docs/FINDINGS.md +2 -0
- docs/PROVISIONAL_TODO.md +2 -0
- docs/RESEARCH_LEDGER.md +4 -0
- docs/REVERSE_ENGINEERING_TODO.md +2 -0
- docs/research/README.md +2 -0
- docs/research/RUST_EFFECT_DEFUSE_FINISH.md +5 -0
- docs/research/rust-effect-defuse-finish.json +0 -0
- docs/research/rust-health-impulse.json +1 -1
- scripts/analyze_rust_effect_defuse_finish.py +20 -0
- scripts/test_rust_effect_defuse_finish.py +9 -0
- xtask/checks.tsv +37 -0
- xtask/src/lib.rs +4 -0
crates/urt-gameplay/src/bin/urt-effect-feedback.rs
CHANGED
|
@@ -27,6 +27,7 @@ fn main() {
|
|
| 27 |
);
|
| 28 |
}
|
| 29 |
"radio" => println!("{}", radio(&w)),
|
|
|
|
| 30 |
_ => panic!("unknown contract"),
|
| 31 |
}
|
| 32 |
}
|
|
@@ -81,3 +82,187 @@ fn radio(w: &[u32]) -> String {
|
|
| 81 |
list(&m)
|
| 82 |
)
|
| 83 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
);
|
| 28 |
}
|
| 29 |
"radio" => println!("{}", radio(&w)),
|
| 30 |
+
"finish" => println!("{}", fragment(&w, DefusePhase::Finish)),
|
| 31 |
_ => panic!("unknown contract"),
|
| 32 |
}
|
| 33 |
}
|
|
|
|
| 82 |
list(&m)
|
| 83 |
)
|
| 84 |
}
|
| 85 |
+
fn decode_defuse(
|
| 86 |
+
w: &[u32],
|
| 87 |
+
) -> (
|
| 88 |
+
urt_gameplay::defuse::DefuseState,
|
| 89 |
+
urt_gameplay::defuse::DefuseInput,
|
| 90 |
+
) {
|
| 91 |
+
use urt_gameplay::defuse::*;
|
| 92 |
+
(
|
| 93 |
+
DefuseState {
|
| 94 |
+
flags: w[0],
|
| 95 |
+
flags104: w[1],
|
| 96 |
+
busy: w[2],
|
| 97 |
+
clock: w[3],
|
| 98 |
+
complete: w[4],
|
| 99 |
+
animation: w[5],
|
| 100 |
+
weapon: w[6],
|
| 101 |
+
event: [7, 0x80000001],
|
| 102 |
+
},
|
| 103 |
+
DefuseInput {
|
| 104 |
+
tick: w[7],
|
| 105 |
+
duration: w[8],
|
| 106 |
+
index: w[9],
|
| 107 |
+
slot: 2,
|
| 108 |
+
},
|
| 109 |
+
)
|
| 110 |
+
}
|
| 111 |
+
fn feedback(marker: bool) -> FeedbackState {
|
| 112 |
+
let mut counters = (100..115).collect::<Vec<_>>();
|
| 113 |
+
counters[5] = u32::MAX;
|
| 114 |
+
FeedbackState {
|
| 115 |
+
score: urt_gameplay::score::State {
|
| 116 |
+
player: -1,
|
| 117 |
+
teams: [100, 101, 102, 103],
|
| 118 |
+
},
|
| 119 |
+
counters,
|
| 120 |
+
marker: if marker {
|
| 121 |
+
Some(RadioMarker {
|
| 122 |
+
deadline: 17,
|
| 123 |
+
active: 18,
|
| 124 |
+
})
|
| 125 |
+
} else {
|
| 126 |
+
None
|
| 127 |
+
},
|
| 128 |
+
event_record: None,
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
fn effect_events(v: &[ResolvedEffect]) -> String {
|
| 132 |
+
v.iter()
|
| 133 |
+
.map(|e| match e {
|
| 134 |
+
ResolvedEffect::Log { format, slot } => {
|
| 135 |
+
format!(r#"{{"op":"log","format":"{}","slot":{slot}}}"#, hex(format))
|
| 136 |
+
}
|
| 137 |
+
ResolvedEffect::Allocate { event } => format!(r#"{{"op":"allocate","event":{event}}}"#),
|
| 138 |
+
ResolvedEffect::Link => r#"{"op":"link"}"#.into(),
|
| 139 |
+
ResolvedEffect::Score { delta } => format!(r#"{{"op":"score","delta":{delta}}}"#),
|
| 140 |
+
ResolvedEffect::Rank => r#"{"op":"rank"}"#.into(),
|
| 141 |
+
ResolvedEffect::Counter { index } => format!(r#"{{"op":"counter","index":{index}}}"#),
|
| 142 |
+
ResolvedEffect::Radio { code } => format!(r#"{{"op":"radio","code":{code}}}"#),
|
| 143 |
+
})
|
| 144 |
+
.collect::<Vec<_>>()
|
| 145 |
+
.join(",")
|
| 146 |
+
}
|
| 147 |
+
fn event_record(record: &Option<urt_gameplay::transition::EventRecord>) -> String {
|
| 148 |
+
let Some(e) = record else {
|
| 149 |
+
return "null".into();
|
| 150 |
+
};
|
| 151 |
+
let keys = [
|
| 152 |
+
4, 12, 16, 20, 24, 28, 32, 36, 40, 44, 84, 140, 400, 424, 488, 492, 496, 536, 540, 568, 572,
|
| 153 |
+
];
|
| 154 |
+
let values = [
|
| 155 |
+
e.kind,
|
| 156 |
+
e.trajectory[0],
|
| 157 |
+
e.trajectory[1],
|
| 158 |
+
e.trajectory[2],
|
| 159 |
+
e.origin[0],
|
| 160 |
+
e.origin[1],
|
| 161 |
+
e.origin[2],
|
| 162 |
+
e.velocity[0],
|
| 163 |
+
e.velocity[1],
|
| 164 |
+
e.velocity[2],
|
| 165 |
+
e.caller_time,
|
| 166 |
+
e.owner,
|
| 167 |
+
e.field400,
|
| 168 |
+
e.flags,
|
| 169 |
+
e.current_origin[0],
|
| 170 |
+
e.current_origin[1],
|
| 171 |
+
e.current_origin[2],
|
| 172 |
+
e.class_token,
|
| 173 |
+
e.class_hash,
|
| 174 |
+
e.event_time as u32,
|
| 175 |
+
e.free_after_event,
|
| 176 |
+
];
|
| 177 |
+
format!(
|
| 178 |
+
"{{{}}}",
|
| 179 |
+
keys.iter()
|
| 180 |
+
.zip(values)
|
| 181 |
+
.map(|(k, v)| format!("\"{k}\":{v}"))
|
| 182 |
+
.collect::<Vec<_>>()
|
| 183 |
+
.join(",")
|
| 184 |
+
)
|
| 185 |
+
}
|
| 186 |
+
fn feedback_fields(
|
| 187 |
+
s: urt_gameplay::defuse::DefuseState,
|
| 188 |
+
f: &FeedbackState,
|
| 189 |
+
o: &ResolvedOutput,
|
| 190 |
+
) -> String {
|
| 191 |
+
let marker = f.marker.map(|v| [v.deadline, v.active]).unwrap_or([17, 18]);
|
| 192 |
+
let radio = o
|
| 193 |
+
.radio
|
| 194 |
+
.iter()
|
| 195 |
+
.map(|r| {
|
| 196 |
+
format!(
|
| 197 |
+
r#"{{"commands":[{}],"getter_calls":{},"marker":{}}}"#,
|
| 198 |
+
commands(&r.commands),
|
| 199 |
+
r.getter_calls,
|
| 200 |
+
list(&marker)
|
| 201 |
+
)
|
| 202 |
+
})
|
| 203 |
+
.collect::<Vec<_>>()
|
| 204 |
+
.join(",");
|
| 205 |
+
format!(
|
| 206 |
+
r#""flags":{},"flags104":{},"busy":{},"clock":{},"complete":{},"animation":{},"weapon":{},"event":{},"guards":[2864434397,287454020],"events":[{}],"score":{},"teams":{},"counters":{},"radio":[{}],"marker":{},"event_record":{}"#,
|
| 207 |
+
s.flags,
|
| 208 |
+
s.flags104,
|
| 209 |
+
s.busy,
|
| 210 |
+
s.clock,
|
| 211 |
+
s.complete,
|
| 212 |
+
s.animation,
|
| 213 |
+
s.weapon,
|
| 214 |
+
list(&s.event),
|
| 215 |
+
effect_events(&o.effects),
|
| 216 |
+
f.score.player as u32,
|
| 217 |
+
list(&f.score.teams.map(|v| v as u32)),
|
| 218 |
+
list(&f.counters),
|
| 219 |
+
radio,
|
| 220 |
+
list(&marker),
|
| 221 |
+
event_record(&f.event_record)
|
| 222 |
+
)
|
| 223 |
+
}
|
| 224 |
+
fn fragment(w: &[u32], phase: DefusePhase) -> String {
|
| 225 |
+
assert!(w.len() >= 18);
|
| 226 |
+
let n = w[17] as usize;
|
| 227 |
+
assert_eq!(w.len(), 18 + n);
|
| 228 |
+
let (mut s, i) = decode_defuse(w);
|
| 229 |
+
let mut f = feedback(w[12] != 0);
|
| 230 |
+
let c = EffectContext {
|
| 231 |
+
paused: w[10] != 0,
|
| 232 |
+
constructed: w[11] != 0,
|
| 233 |
+
tick: w[7],
|
| 234 |
+
origin: w[14..17].try_into().unwrap(),
|
| 235 |
+
radio: RadioInput {
|
| 236 |
+
follow: 0,
|
| 237 |
+
health: 1,
|
| 238 |
+
team: 1,
|
| 239 |
+
slot: 2,
|
| 240 |
+
mode: 8,
|
| 241 |
+
count: 4,
|
| 242 |
+
tick: w[7],
|
| 243 |
+
code: 7,
|
| 244 |
+
success: w[13] != 0,
|
| 245 |
+
payload: w[18..].iter().map(|v| *v as u8).collect(),
|
| 246 |
+
},
|
| 247 |
+
clients: vec![
|
| 248 |
+
RadioClient {
|
| 249 |
+
connection: 2,
|
| 250 |
+
team: 1,
|
| 251 |
+
},
|
| 252 |
+
RadioClient {
|
| 253 |
+
connection: 2,
|
| 254 |
+
team: 2,
|
| 255 |
+
},
|
| 256 |
+
RadioClient {
|
| 257 |
+
connection: 2,
|
| 258 |
+
team: 1,
|
| 259 |
+
},
|
| 260 |
+
RadioClient {
|
| 261 |
+
connection: 1,
|
| 262 |
+
team: 2,
|
| 263 |
+
},
|
| 264 |
+
],
|
| 265 |
+
};
|
| 266 |
+
let o = resolve_defuse_fragment(&mut s, &mut f, i, &c, phase).unwrap();
|
| 267 |
+
format!("{{{}}}", feedback_fields(s, &f, &o))
|
| 268 |
+
}
|
crates/urt-gameplay/src/effect_feedback.rs
CHANGED
|
@@ -181,3 +181,235 @@ mod radio_tests {
|
|
| 181 |
assert_eq!(m, old);
|
| 182 |
}
|
| 183 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
assert_eq!(m, old);
|
| 182 |
}
|
| 183 |
}
|
| 184 |
+
|
| 185 |
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
| 186 |
+
pub struct FeedbackState {
|
| 187 |
+
pub score: crate::score::State,
|
| 188 |
+
pub counters: Vec<u32>,
|
| 189 |
+
pub marker: Option<RadioMarker>,
|
| 190 |
+
pub event_record: Option<crate::transition::EventRecord>,
|
| 191 |
+
}
|
| 192 |
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
| 193 |
+
pub struct EffectContext {
|
| 194 |
+
pub paused: bool,
|
| 195 |
+
pub constructed: bool,
|
| 196 |
+
pub tick: u32,
|
| 197 |
+
pub origin: [u32; 3],
|
| 198 |
+
pub radio: RadioInput,
|
| 199 |
+
pub clients: Vec<RadioClient>,
|
| 200 |
+
}
|
| 201 |
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
| 202 |
+
pub enum ResolvedEffect {
|
| 203 |
+
Log { format: Vec<u8>, slot: i32 },
|
| 204 |
+
Allocate { event: u32 },
|
| 205 |
+
Link,
|
| 206 |
+
Score { delta: i32 },
|
| 207 |
+
Rank,
|
| 208 |
+
Counter { index: usize },
|
| 209 |
+
Radio { code: u32 },
|
| 210 |
+
}
|
| 211 |
+
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
| 212 |
+
pub struct ResolvedOutput {
|
| 213 |
+
pub effects: Vec<ResolvedEffect>,
|
| 214 |
+
pub radio: Vec<RadioOutcome>,
|
| 215 |
+
}
|
| 216 |
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
| 217 |
+
pub enum DefusePhase {
|
| 218 |
+
Begin,
|
| 219 |
+
Finish,
|
| 220 |
+
}
|
| 221 |
+
fn allocated_event() -> crate::transition::EventRecord {
|
| 222 |
+
let v = 0x13579bdf;
|
| 223 |
+
crate::transition::EventRecord {
|
| 224 |
+
kind: v,
|
| 225 |
+
trajectory: [v; 3],
|
| 226 |
+
origin: [v; 3],
|
| 227 |
+
velocity: [v; 3],
|
| 228 |
+
caller_time: v,
|
| 229 |
+
owner: v,
|
| 230 |
+
field400: v,
|
| 231 |
+
flags: 0x80000001,
|
| 232 |
+
current_origin: [v; 3],
|
| 233 |
+
class_token: v,
|
| 234 |
+
class_hash: v,
|
| 235 |
+
event_time: v as i32,
|
| 236 |
+
free_after_event: v,
|
| 237 |
+
}
|
| 238 |
+
}
|
| 239 |
+
fn resolve_effects(
|
| 240 |
+
state: &crate::defuse::DefuseState,
|
| 241 |
+
feedback: &mut FeedbackState,
|
| 242 |
+
effects: Vec<crate::defuse::DefuseEffect>,
|
| 243 |
+
context: &EffectContext,
|
| 244 |
+
) -> Result<ResolvedOutput, Error> {
|
| 245 |
+
use crate::defuse::DefuseEffect;
|
| 246 |
+
let mut out = ResolvedOutput::default();
|
| 247 |
+
for effect in effects {
|
| 248 |
+
match effect {
|
| 249 |
+
DefuseEffect::Log { format, slot } => {
|
| 250 |
+
out.effects.push(ResolvedEffect::Log { format, slot })
|
| 251 |
+
}
|
| 252 |
+
DefuseEffect::Allocate { event } => {
|
| 253 |
+
out.effects.push(ResolvedEffect::Allocate { event });
|
| 254 |
+
if context.constructed {
|
| 255 |
+
let mut record = feedback
|
| 256 |
+
.event_record
|
| 257 |
+
.clone()
|
| 258 |
+
.unwrap_or_else(allocated_event);
|
| 259 |
+
crate::transition::temporary_event(
|
| 260 |
+
&mut record,
|
| 261 |
+
context.origin,
|
| 262 |
+
event,
|
| 263 |
+
context.tick as i32,
|
| 264 |
+
)
|
| 265 |
+
.map_err(|_| Error::InvalidInput)?;
|
| 266 |
+
feedback.event_record = Some(record);
|
| 267 |
+
out.effects.push(ResolvedEffect::Link);
|
| 268 |
+
}
|
| 269 |
+
}
|
| 270 |
+
// Compatibility tag Teleport is numeric205260: score addition, origin unused.
|
| 271 |
+
DefuseEffect::Teleport { .. } => {
|
| 272 |
+
out.effects.push(ResolvedEffect::Score { delta: 1 });
|
| 273 |
+
let (score, rank) =
|
| 274 |
+
crate::score::add(true, context.paused, 8, false, 1, 1, feedback.score)
|
| 275 |
+
.map_err(|_| Error::InvalidInput)?;
|
| 276 |
+
feedback.score = score;
|
| 277 |
+
if rank {
|
| 278 |
+
out.effects.push(ResolvedEffect::Rank);
|
| 279 |
+
}
|
| 280 |
+
}
|
| 281 |
+
// Compatibility tag Award is numeric283463: increment a counter index once.
|
| 282 |
+
DefuseEffect::Award { value, .. } => {
|
| 283 |
+
increment_objective_counter(&mut feedback.counters, value as usize)?;
|
| 284 |
+
out.effects.push(ResolvedEffect::Counter {
|
| 285 |
+
index: value as usize,
|
| 286 |
+
});
|
| 287 |
+
}
|
| 288 |
+
DefuseEffect::Sound { slot, sound } => {
|
| 289 |
+
out.effects.push(ResolvedEffect::Radio { code: sound });
|
| 290 |
+
let mut input = context.radio.clone();
|
| 291 |
+
input.slot = slot;
|
| 292 |
+
input.code = sound;
|
| 293 |
+
out.radio
|
| 294 |
+
.push(team_radio(&mut feedback.marker, &context.clients, &input)?);
|
| 295 |
+
}
|
| 296 |
+
}
|
| 297 |
+
}
|
| 298 |
+
if let Some(record) = &mut feedback.event_record {
|
| 299 |
+
record.flags = state.event[1];
|
| 300 |
+
}
|
| 301 |
+
Ok(out)
|
| 302 |
+
}
|
| 303 |
+
/// Defuse fragments with actual score/counter/radio/event-constructor policies.
|
| 304 |
+
/// Ranking, native sends/linking and base event allocation remain caller work.
|
| 305 |
+
pub fn resolve_defuse_fragment(
|
| 306 |
+
state: &mut crate::defuse::DefuseState,
|
| 307 |
+
feedback: &mut FeedbackState,
|
| 308 |
+
input: crate::defuse::DefuseInput,
|
| 309 |
+
context: &EffectContext,
|
| 310 |
+
phase: DefusePhase,
|
| 311 |
+
) -> Result<ResolvedOutput, Error> {
|
| 312 |
+
let mut next = *state;
|
| 313 |
+
let mut fb = feedback.clone();
|
| 314 |
+
let effects = match phase {
|
| 315 |
+
DefusePhase::Begin => crate::defuse::begin_defuse(&mut next, input)?,
|
| 316 |
+
DefusePhase::Finish => crate::defuse::finish_defuse(&mut next, input)?,
|
| 317 |
+
};
|
| 318 |
+
let out = resolve_effects(&next, &mut fb, effects, context)?;
|
| 319 |
+
*state = next;
|
| 320 |
+
*feedback = fb;
|
| 321 |
+
Ok(out)
|
| 322 |
+
}
|
| 323 |
+
#[cfg(test)]
|
| 324 |
+
mod resolved_finish_tests {
|
| 325 |
+
use super::*;
|
| 326 |
+
pub(super) fn state() -> crate::defuse::DefuseState {
|
| 327 |
+
crate::defuse::DefuseState {
|
| 328 |
+
flags: 0x80008004,
|
| 329 |
+
flags104: 16,
|
| 330 |
+
busy: 1,
|
| 331 |
+
clock: 0,
|
| 332 |
+
complete: 0,
|
| 333 |
+
animation: 99,
|
| 334 |
+
weapon: 11,
|
| 335 |
+
event: [7, 0x80000001],
|
| 336 |
+
}
|
| 337 |
+
}
|
| 338 |
+
pub(super) fn feedback() -> FeedbackState {
|
| 339 |
+
let mut counters = (100..115).collect::<Vec<_>>();
|
| 340 |
+
counters[5] = u32::MAX;
|
| 341 |
+
FeedbackState {
|
| 342 |
+
score: crate::score::State {
|
| 343 |
+
player: -1,
|
| 344 |
+
teams: [100, 101, 102, 103],
|
| 345 |
+
},
|
| 346 |
+
counters,
|
| 347 |
+
marker: Some(RadioMarker {
|
| 348 |
+
deadline: 17,
|
| 349 |
+
active: 18,
|
| 350 |
+
}),
|
| 351 |
+
event_record: None,
|
| 352 |
+
}
|
| 353 |
+
}
|
| 354 |
+
pub(super) fn context() -> EffectContext {
|
| 355 |
+
EffectContext {
|
| 356 |
+
paused: false,
|
| 357 |
+
constructed: false,
|
| 358 |
+
tick: 10000,
|
| 359 |
+
origin: [0; 3],
|
| 360 |
+
radio: RadioInput {
|
| 361 |
+
follow: 0,
|
| 362 |
+
health: 1,
|
| 363 |
+
team: 1,
|
| 364 |
+
slot: 2,
|
| 365 |
+
mode: 8,
|
| 366 |
+
count: 3,
|
| 367 |
+
tick: 10000,
|
| 368 |
+
code: 7,
|
| 369 |
+
success: true,
|
| 370 |
+
payload: b"Bomb site".to_vec(),
|
| 371 |
+
},
|
| 372 |
+
clients: vec![
|
| 373 |
+
RadioClient {
|
| 374 |
+
connection: 2,
|
| 375 |
+
team: 1
|
| 376 |
+
};
|
| 377 |
+
3
|
| 378 |
+
],
|
| 379 |
+
}
|
| 380 |
+
}
|
| 381 |
+
pub(super) fn input() -> crate::defuse::DefuseInput {
|
| 382 |
+
crate::defuse::DefuseInput {
|
| 383 |
+
tick: 10000,
|
| 384 |
+
duration: 5,
|
| 385 |
+
index: 3,
|
| 386 |
+
slot: 2,
|
| 387 |
+
}
|
| 388 |
+
}
|
| 389 |
+
#[test]
|
| 390 |
+
fn paused_completion_increments_counter_but_not_score() {
|
| 391 |
+
let mut s = state();
|
| 392 |
+
let mut f = feedback();
|
| 393 |
+
let c = EffectContext {
|
| 394 |
+
paused: true,
|
| 395 |
+
..context()
|
| 396 |
+
};
|
| 397 |
+
let o = resolve_defuse_fragment(&mut s, &mut f, input(), &c, DefusePhase::Finish).unwrap();
|
| 398 |
+
assert_eq!(f.score.player, -1);
|
| 399 |
+
assert_eq!(f.counters[5], 0);
|
| 400 |
+
assert!(!o.effects.contains(&ResolvedEffect::Rank));
|
| 401 |
+
assert_eq!(s.complete, 1);
|
| 402 |
+
}
|
| 403 |
+
#[test]
|
| 404 |
+
fn unsupported_counter_rolls_back_prior_completion_and_score() {
|
| 405 |
+
let mut s = state();
|
| 406 |
+
let mut f = feedback();
|
| 407 |
+
f.counters.clear();
|
| 408 |
+
let old = (s, f.clone());
|
| 409 |
+
assert!(
|
| 410 |
+
resolve_defuse_fragment(&mut s, &mut f, input(), &context(), DefusePhase::Finish)
|
| 411 |
+
.is_err()
|
| 412 |
+
);
|
| 413 |
+
assert_eq!((s, f), old);
|
| 414 |
+
}
|
| 415 |
+
}
|
docs/FINDINGS.md
CHANGED
|
@@ -1997,3 +1997,5 @@ RE-1026: Thirty-four original-QVM scenarios recover193620 with actual mode class
|
|
| 1997 |
RE-1027: Rust matches34 source team-radio scenarios, including the mode9 exception, mode11/signed exclusions, failed location text, raw quote preservation and64-recipient tables. Only actual sends update an existing marker; empty routing leaves it unchanged. Tests cover connected same-team routing, wrapped marker deadlines and invalid payload atomicity. Text location and native network sends remain boundaries. [Evidence](research/RUST_EFFECT_RADIO.md).
|
| 1998 |
|
| 1999 |
RE-1028: Sixty-four source probes execute defuse completion with actual205260 and283463. Ready completion adds one player score point and requests ranking unless paused, then increments counter index5 by1 regardless of pause. Both score and counter wrap independently. Waiting does neither. Corrected ordered requests are log, allocate, score, optional rank, counter; allocation/log/ranking remain boundaries. This replaces the earlier misleading teleport/five-point labels with actual state feedback. [Evidence](research/QVM_EFFECT_DEFUSE_FINISH.md).
|
|
|
|
|
|
|
|
|
| 1997 |
RE-1027: Rust matches34 source team-radio scenarios, including the mode9 exception, mode11/signed exclusions, failed location text, raw quote preservation and64-recipient tables. Only actual sends update an existing marker; empty routing leaves it unchanged. Tests cover connected same-team routing, wrapped marker deadlines and invalid payload atomicity. Text location and native network sends remain boundaries. [Evidence](research/RUST_EFFECT_RADIO.md).
|
| 1998 |
|
| 1999 |
RE-1028: Sixty-four source probes execute defuse completion with actual205260 and283463. Ready completion adds one player score point and requests ranking unless paused, then increments counter index5 by1 regardless of pause. Both score and counter wrap independently. Waiting does neither. Corrected ordered requests are log, allocate, score, optional rank, counter; allocation/log/ranking remain boundaries. This replaces the earlier misleading teleport/five-point labels with actual state feedback. [Evidence](research/QVM_EFFECT_DEFUSE_FINISH.md).
|
| 2000 |
+
|
| 2001 |
+
RE-1029: Rust executes the existing score policy and indexed counter update for defuse completion across64 source cases. Paused score remains unchanged without a ranking request, while counter index5 wraps independently. Correct semantic trace tags replace historical aliases in these new reports. Whole completion and feedback state stage atomically; unit tests cover paused behavior and late counter failure after a planned score update. Ranking/log/allocation remain boundaries. [Evidence](research/RUST_EFFECT_DEFUSE_FINISH.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -2057,3 +2057,5 @@ RE-974–983 completed routing, command rewrite, inactivity, movement-parameter
|
|
| 2057 |
- [x] RE-1027 Port team radio command planning and marker feedback ([evidence](research/RUST_EFFECT_RADIO.md)).
|
| 2058 |
|
| 2059 |
- [x] RE-1028 Resolve defuse completion score and objective counter ([evidence](research/QVM_EFFECT_DEFUSE_FINISH.md)).
|
|
|
|
|
|
|
|
|
| 2057 |
- [x] RE-1027 Port team radio command planning and marker feedback ([evidence](research/RUST_EFFECT_RADIO.md)).
|
| 2058 |
|
| 2059 |
- [x] RE-1028 Resolve defuse completion score and objective counter ([evidence](research/QVM_EFFECT_DEFUSE_FINISH.md)).
|
| 2060 |
+
|
| 2061 |
+
- [x] RE-1029 Port resolved defuse score and counter feedback ([evidence](research/RUST_EFFECT_DEFUSE_FINISH.md)).
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -2787,3 +2787,7 @@ Rust matches34 source team-radio scenarios, including the mode9 exception, mode1
|
|
| 2787 |
## 2026-10-29 — RE-1028 Resolve defuse completion score and objective counter
|
| 2788 |
|
| 2789 |
Sixty-four source probes execute defuse completion with actual205260 and283463. Ready completion adds one player score point and requests ranking unless paused, then increments counter index5 by1 regardless of pause. Both score and counter wrap independently. Waiting does neither. Corrected ordered requests are log, allocate, score, optional rank, counter; allocation/log/ranking remain boundaries. This replaces the earlier misleading teleport/five-point labels with actual state feedback. Focused `cargo xtask test-group qvm-effect-defuse-finish` passes with deterministic report regeneration and selected regressions. Full suite deferred to task ten. Original assets unchanged; Python uses the flake-declared Nix runtime.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2787 |
## 2026-10-29 — RE-1028 Resolve defuse completion score and objective counter
|
| 2788 |
|
| 2789 |
Sixty-four source probes execute defuse completion with actual205260 and283463. Ready completion adds one player score point and requests ranking unless paused, then increments counter index5 by1 regardless of pause. Both score and counter wrap independently. Waiting does neither. Corrected ordered requests are log, allocate, score, optional rank, counter; allocation/log/ranking remain boundaries. This replaces the earlier misleading teleport/five-point labels with actual state feedback. Focused `cargo xtask test-group qvm-effect-defuse-finish` passes with deterministic report regeneration and selected regressions. Full suite deferred to task ten. Original assets unchanged; Python uses the flake-declared Nix runtime.
|
| 2790 |
+
|
| 2791 |
+
## 2026-10-29 — RE-1029 Port resolved defuse score and counter feedback
|
| 2792 |
+
|
| 2793 |
+
Rust executes the existing score policy and indexed counter update for defuse completion across64 source cases. Paused score remains unchanged without a ranking request, while counter index5 wraps independently. Correct semantic trace tags replace historical aliases in these new reports. Whole completion and feedback state stage atomically; unit tests cover paused behavior and late counter failure after a planned score update. Ranking/log/allocation remain boundaries. Focused `cargo xtask test-group rust-effect-defuse-finish` passes with deterministic report regeneration and selected regressions. Full suite deferred to task ten. Original assets unchanged; Python uses the flake-declared Nix runtime.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -2094,3 +2094,5 @@ RE-974–983 recover routing, command rewriting, inactivity and composed setup t
|
|
| 2094 |
- [x] RE-1027 Port team radio command planning and marker feedback ([evidence](research/RUST_EFFECT_RADIO.md)).
|
| 2095 |
|
| 2096 |
- [x] RE-1028 Resolve defuse completion score and objective counter ([evidence](research/QVM_EFFECT_DEFUSE_FINISH.md)).
|
|
|
|
|
|
|
|
|
| 2094 |
- [x] RE-1027 Port team radio command planning and marker feedback ([evidence](research/RUST_EFFECT_RADIO.md)).
|
| 2095 |
|
| 2096 |
- [x] RE-1028 Resolve defuse completion score and objective counter ([evidence](research/QVM_EFFECT_DEFUSE_FINISH.md)).
|
| 2097 |
+
|
| 2098 |
+
- [x] RE-1029 Port resolved defuse score and counter feedback ([evidence](research/RUST_EFFECT_DEFUSE_FINISH.md)).
|
docs/research/README.md
CHANGED
|
@@ -1625,3 +1625,5 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 1625 |
- [RE-1027: Port team radio command planning and marker feedback](RUST_EFFECT_RADIO.md).
|
| 1626 |
|
| 1627 |
- [RE-1028: Resolve defuse completion score and objective counter](QVM_EFFECT_DEFUSE_FINISH.md).
|
|
|
|
|
|
|
|
|
| 1625 |
- [RE-1027: Port team radio command planning and marker feedback](RUST_EFFECT_RADIO.md).
|
| 1626 |
|
| 1627 |
- [RE-1028: Resolve defuse completion score and objective counter](QVM_EFFECT_DEFUSE_FINISH.md).
|
| 1628 |
+
|
| 1629 |
+
- [RE-1029: Port resolved defuse score and counter feedback](RUST_EFFECT_DEFUSE_FINISH.md).
|
docs/research/RUST_EFFECT_DEFUSE_FINISH.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Port resolved defuse score and counter feedback
|
| 2 |
+
|
| 3 |
+
RE-1029. Focused validation: `cargo xtask test-group rust-effect-defuse-finish`. [Machine report](rust-effect-defuse-finish.json).
|
| 4 |
+
|
| 5 |
+
Rust executes the existing score policy and indexed counter update for defuse completion across64 source cases. Paused score remains unchanged without a ranking request, while counter index5 wraps independently. Correct semantic trace tags replace historical aliases in these new reports. Whole completion and feedback state stage atomically; unit tests cover paused behavior and late counter failure after a planned score update. Ranking/log/allocation remain boundaries.
|
docs/research/rust-effect-defuse-finish.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
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": 285,
|
| 2989 |
"source_report": "docs/research/qvm-health-impulse.json",
|
| 2990 |
"source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
|
| 2991 |
}
|
scripts/analyze_rust_effect_defuse_finish.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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-effect-feedback'],check=True,capture_output=True)
|
| 9 |
+
binary=Path(os.environ.get('CARGO_TARGET_DIR','target'))/'debug'/'urt-effect-feedback'
|
| 10 |
+
path=Path('docs/research/qvm-effect-defuse-finish.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 ['flags', 'flags104', 'busy', 'clock', 'complete', 'animation', 'weapon', 'event', 'guards', 'events', 'score', 'teams', 'counters', 'radio', 'marker', 'event_record']},rust=result(binary,'finish',encode(p['input']))))
|
| 13 |
+
write_json(Path('docs/research/rust-effect-defuse-finish.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 |
+
from analyze_rust_defuse_begin import encode as base_encode
|
| 16 |
+
def encode(a):
|
| 17 |
+
from analyze_qvm_acceleration import word
|
| 18 |
+
payload=bytes.fromhex(a['payload'])
|
| 19 |
+
return base_encode(a)+[a['paused'],a['constructed'],a['marker'],a['success'],*[word(v) for v in a['origin']],len(payload),*payload]
|
| 20 |
+
if __name__=='__main__':main()
|
scripts/test_rust_effect_defuse_finish.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-effect-defuse-finish.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-effect-defuse-finish.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
|
@@ -4400,3 +4400,40 @@ report scripts/analyze_qvm_effect_defuse_finish.py docs/research/qvm-effect-defu
|
|
| 4400 |
edge docs/research/qvm-effect-defuse-finish.json scripts/test_qvm_effect_defuse_finish.py
|
| 4401 |
group qvm-effect-defuse-finish scripts/analyze_qvm_effect_defuse_finish.py
|
| 4402 |
edge scripts/analyze_qvm_effect_defuse_finish.py scripts/analyze_qvm_resumer_audit.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4400 |
edge docs/research/qvm-effect-defuse-finish.json scripts/test_qvm_effect_defuse_finish.py
|
| 4401 |
group qvm-effect-defuse-finish scripts/analyze_qvm_effect_defuse_finish.py
|
| 4402 |
edge scripts/analyze_qvm_effect_defuse_finish.py scripts/analyze_qvm_resumer_audit.py
|
| 4403 |
+
report scripts/analyze_rust_effect_defuse_finish.py docs/research/rust-effect-defuse-finish.json
|
| 4404 |
+
edge docs/research/rust-effect-defuse-finish.json scripts/test_rust_effect_defuse_finish.py
|
| 4405 |
+
group rust-effect-defuse-finish scripts/analyze_rust_effect_defuse_finish.py
|
| 4406 |
+
edge scripts/analyze_rust_effect_defuse_finish.py scripts/analyze_qvm_resumer_audit.py
|
| 4407 |
+
edge Cargo.toml scripts/analyze_rust_effect_defuse_finish.py
|
| 4408 |
+
edge Cargo.lock scripts/analyze_rust_effect_defuse_finish.py
|
| 4409 |
+
edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_effect_defuse_finish.py
|
| 4410 |
+
edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4411 |
+
edge crates/urt-gameplay/src/filter_loading.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4412 |
+
edge crates/urt-gameplay/src/filter_lifecycle.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4413 |
+
edge crates/urt-gameplay/src/native_policy.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4414 |
+
edge crates/urt-gameplay/src/bin/urt-filter-loading.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4415 |
+
edge crates/urt-gameplay/src/qvm_format.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4416 |
+
edge docs/research/qvm-effect-defuse-finish.json scripts/analyze_rust_effect_defuse_finish.py
|
| 4417 |
+
group rust-effect-defuse-finish scripts/analyze_rust_health_impulse.py
|
| 4418 |
+
group gameplay scripts/analyze_rust_effect_defuse_finish.py
|
| 4419 |
+
edge crates/urt-gameplay/src/filter_commands.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4420 |
+
edge crates/urt-gameplay/src/bin/urt-filter-commands.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4421 |
+
edge crates/urt-gameplay/src/client_timer.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4422 |
+
edge crates/urt-gameplay/src/bin/urt-client-timer.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4423 |
+
edge crates/urt-gameplay/src/client_processing.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4424 |
+
edge crates/urt-gameplay/src/bin/urt-client-processing.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4425 |
+
edge crates/urt-gameplay/src/client_move_context.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4426 |
+
edge crates/urt-gameplay/src/bin/urt-client-move-context.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4427 |
+
edge crates/urt-gameplay/src/move_callbacks.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4428 |
+
edge crates/urt-gameplay/src/bin/urt-move-callbacks.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4429 |
+
edge crates/urt-gameplay/src/assistance.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4430 |
+
edge crates/urt-gameplay/src/bin/urt-assistance.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4431 |
+
edge crates/urt-gameplay/src/move_callbacks.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4432 |
+
edge crates/urt-gameplay/src/defuse.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4433 |
+
edge crates/urt-gameplay/src/bin/urt-defuse.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4434 |
+
edge crates/urt-gameplay/src/move_callbacks.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4435 |
+
edge crates/urt-gameplay/src/effect_feedback.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4436 |
+
edge crates/urt-gameplay/src/bin/urt-effect-feedback.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4437 |
+
edge crates/urt-gameplay/src/score.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4438 |
+
edge crates/urt-gameplay/src/transition.rs scripts/analyze_rust_effect_defuse_finish.py
|
| 4439 |
+
edge crates/urt-gameplay/src/defuse.rs scripts/analyze_rust_effect_defuse_finish.py
|
xtask/src/lib.rs
CHANGED
|
@@ -1168,6 +1168,9 @@ mod integration_tests {
|
|
| 1168 |
("effect_feedback", "effect_radio"),
|
| 1169 |
("score", "effect_radio"),
|
| 1170 |
("transition", "effect_radio"),
|
|
|
|
|
|
|
|
|
|
| 1171 |
("client_move_context", "client_move_scratch"),
|
| 1172 |
("client_processing", "client_routing"),
|
| 1173 |
("client_timer", "client_routing"),
|
|
@@ -1403,6 +1406,7 @@ mod integration_tests {
|
|
| 1403 |
("qvm-defuse-sequence", "defuse_sequence"),
|
| 1404 |
("qvm-effect-counter", "effect_counter"),
|
| 1405 |
("qvm-effect-radio", "effect_radio"),
|
|
|
|
| 1406 |
("qvm-client-move-scratch", "client_move_scratch"),
|
| 1407 |
("qvm-client-routing", "client_routing"),
|
| 1408 |
("qvm-client-command-rewrite", "client_command_rewrite"),
|
|
|
|
| 1168 |
("effect_feedback", "effect_radio"),
|
| 1169 |
("score", "effect_radio"),
|
| 1170 |
("transition", "effect_radio"),
|
| 1171 |
+
("effect_feedback", "effect_defuse_finish"),
|
| 1172 |
+
("score", "effect_defuse_finish"),
|
| 1173 |
+
("transition", "effect_defuse_finish"),
|
| 1174 |
("client_move_context", "client_move_scratch"),
|
| 1175 |
("client_processing", "client_routing"),
|
| 1176 |
("client_timer", "client_routing"),
|
|
|
|
| 1406 |
("qvm-defuse-sequence", "defuse_sequence"),
|
| 1407 |
("qvm-effect-counter", "effect_counter"),
|
| 1408 |
("qvm-effect-radio", "effect_radio"),
|
| 1409 |
+
("qvm-effect-defuse-finish", "effect_defuse_finish"),
|
| 1410 |
("qvm-client-move-scratch", "client_move_scratch"),
|
| 1411 |
("qvm-client-routing", "client_routing"),
|
| 1412 |
("qvm-client-command-rewrite", "client_command_rewrite"),
|