gameplay: port connect completion callback order
Browse files- crates/urt-gameplay/src/bin/urt-native-policy.rs +91 -0
- crates/urt-gameplay/src/native_policy.rs +196 -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_CONNECT_TAIL.md +7 -0
- docs/research/rust-connect-tail.json +0 -0
- docs/research/rust-health-impulse.json +1 -1
- scripts/analyze_rust_connect_tail.py +14 -0
- scripts/test_rust_connect_tail.py +9 -0
- xtask/checks.tsv +15 -0
crates/urt-gameplay/src/bin/urt-native-policy.rs
CHANGED
|
@@ -843,6 +843,36 @@ fn run() -> Result<String, String> {
|
|
| 843 |
.map_err(|e| format!("{e:?}"))?;
|
| 844 |
Ok(admission_json(&out, ring))
|
| 845 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 846 |
_ => Err("unknown command".into()),
|
| 847 |
}
|
| 848 |
}
|
|
@@ -1167,3 +1197,64 @@ fn admission_json(out: &Admission, ring: u32) -> String {
|
|
| 1167 |
out.accepted
|
| 1168 |
)
|
| 1169 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 843 |
.map_err(|e| format!("{e:?}"))?;
|
| 844 |
Ok(admission_json(&out, ring))
|
| 845 |
}
|
| 846 |
+
"connect-tail" => {
|
| 847 |
+
if w.len() != 8 {
|
| 848 |
+
return Err("invalid tail words".into());
|
| 849 |
+
}
|
| 850 |
+
let mut client = 0xaabbccddu32.to_le_bytes().repeat(1584);
|
| 851 |
+
client[3096..3100].copy_from_slice(&w[1].to_le_bytes());
|
| 852 |
+
client[504..516].fill(0);
|
| 853 |
+
client[504..507].copy_from_slice(b"old");
|
| 854 |
+
let mut entity = ConnectTailEntity {
|
| 855 |
+
flags: 32,
|
| 856 |
+
in_use: 0,
|
| 857 |
+
};
|
| 858 |
+
let out = finish_connect(
|
| 859 |
+
&mut client,
|
| 860 |
+
&mut entity,
|
| 861 |
+
ConnectTailInput {
|
| 862 |
+
mode: w[0] as i32,
|
| 863 |
+
slot: 3,
|
| 864 |
+
first_time: w[3] as i32,
|
| 865 |
+
bot: w[2] as i32,
|
| 866 |
+
bot_answer: w[4] as i32,
|
| 867 |
+
clock: w[6] as i32,
|
| 868 |
+
seconds: w[7] as i32,
|
| 869 |
+
after_team: if w[5] != 0 { Some(2) } else { None },
|
| 870 |
+
name: b"new^1name",
|
| 871 |
+
},
|
| 872 |
+
)
|
| 873 |
+
.map_err(|e| format!("{e:?}"))?;
|
| 874 |
+
Ok(tail_json(&out, &client, &entity))
|
| 875 |
+
}
|
| 876 |
_ => Err("unknown command".into()),
|
| 877 |
}
|
| 878 |
}
|
|
|
|
| 1197 |
out.accepted
|
| 1198 |
)
|
| 1199 |
}
|
| 1200 |
+
|
| 1201 |
+
fn tail_events(events: &[ConnectTailEvent]) -> String {
|
| 1202 |
+
format!(
|
| 1203 |
+
"[{}]",
|
| 1204 |
+
events
|
| 1205 |
+
.iter()
|
| 1206 |
+
.map(|e| match e {
|
| 1207 |
+
ConnectTailEvent::Classify(mode) => format!(r#"{{"op":"classify","mode":{mode}}}"#),
|
| 1208 |
+
ConnectTailEvent::Bot { slot, restart } => format!(
|
| 1209 |
+
r#"{{"op":"bot","slot":{slot},"restart":{}}}"#,
|
| 1210 |
+
u32::from(*restart)
|
| 1211 |
+
),
|
| 1212 |
+
ConnectTailEvent::Zero { offset, length } =>
|
| 1213 |
+
format!(r#"{{"op":"zero","offset":{offset},"length":{length}}}"#),
|
| 1214 |
+
ConnectTailEvent::Log(slot) => format!(r#"{{"op":"log","slot":{slot}}}"#),
|
| 1215 |
+
ConnectTailEvent::Userinfo(slot) => format!(r#"{{"op":"userinfo","slot":{slot}}}"#),
|
| 1216 |
+
ConnectTailEvent::Format => r#"{"op":"format"}"#.into(),
|
| 1217 |
+
ConnectTailEvent::Send { recipient, text } => format!(
|
| 1218 |
+
r#"{{"op":"send","recipient":{recipient},"text":"{}"}}"#,
|
| 1219 |
+
hex(text)
|
| 1220 |
+
),
|
| 1221 |
+
ConnectTailEvent::Join { slot, choice } =>
|
| 1222 |
+
format!(r#"{{"op":"join","slot":{slot},"choice":{choice}}}"#),
|
| 1223 |
+
ConnectTailEvent::Rank => r#"{"op":"rank"}"#.into(),
|
| 1224 |
+
})
|
| 1225 |
+
.collect::<Vec<_>>()
|
| 1226 |
+
.join(",")
|
| 1227 |
+
)
|
| 1228 |
+
}
|
| 1229 |
+
fn watched_words(client: &[u8]) -> String {
|
| 1230 |
+
let mut offsets = vec![504, 508, 512, 516, 3096, 3140];
|
| 1231 |
+
offsets.extend((668..696).step_by(4));
|
| 1232 |
+
offsets.extend((3204..3592).step_by(4));
|
| 1233 |
+
format!(
|
| 1234 |
+
"{{{}}}",
|
| 1235 |
+
offsets
|
| 1236 |
+
.iter()
|
| 1237 |
+
.map(|o| format!(
|
| 1238 |
+
r#""{o}":{}"#,
|
| 1239 |
+
u32::from_le_bytes(client[*o..*o + 4].try_into().unwrap())
|
| 1240 |
+
))
|
| 1241 |
+
.collect::<Vec<_>>()
|
| 1242 |
+
.join(",")
|
| 1243 |
+
)
|
| 1244 |
+
}
|
| 1245 |
+
fn tail_json(out: &ConnectTail, client: &[u8], entity: &ConnectTailEntity) -> String {
|
| 1246 |
+
let msg = out
|
| 1247 |
+
.message
|
| 1248 |
+
.as_ref()
|
| 1249 |
+
.map(|v| format!(r#""{}""#, hex(v)))
|
| 1250 |
+
.unwrap_or("null".into());
|
| 1251 |
+
format!(
|
| 1252 |
+
r#"{{"accepted":{},"message":{msg},"events":{},"words":{},"entity_flags":{},"in_use":{},"ring":{}}}"#,
|
| 1253 |
+
out.accepted,
|
| 1254 |
+
tail_events(&out.events),
|
| 1255 |
+
watched_words(client),
|
| 1256 |
+
entity.flags,
|
| 1257 |
+
entity.in_use as u32,
|
| 1258 |
+
out.ring_advance
|
| 1259 |
+
)
|
| 1260 |
+
}
|
crates/urt-gameplay/src/native_policy.rs
CHANGED
|
@@ -2319,3 +2319,199 @@ mod admission_tests {
|
|
| 2319 |
assert_eq!(r, 1);
|
| 2320 |
}
|
| 2321 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2319 |
assert_eq!(r, 1);
|
| 2320 |
}
|
| 2321 |
}
|
| 2322 |
+
|
| 2323 |
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
| 2324 |
+
pub struct ConnectTailEntity {
|
| 2325 |
+
pub flags: u32,
|
| 2326 |
+
pub in_use: i32,
|
| 2327 |
+
}
|
| 2328 |
+
pub struct ConnectTailInput<'a> {
|
| 2329 |
+
pub mode: i32,
|
| 2330 |
+
pub slot: usize,
|
| 2331 |
+
pub first_time: i32,
|
| 2332 |
+
pub bot: i32,
|
| 2333 |
+
pub bot_answer: i32,
|
| 2334 |
+
pub clock: i32,
|
| 2335 |
+
pub seconds: i32,
|
| 2336 |
+
pub after_team: Option<i32>,
|
| 2337 |
+
pub name: &'a [u8],
|
| 2338 |
+
}
|
| 2339 |
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
| 2340 |
+
pub enum ConnectTailEvent {
|
| 2341 |
+
Classify(i32),
|
| 2342 |
+
Bot { slot: usize, restart: bool },
|
| 2343 |
+
Zero { offset: usize, length: usize },
|
| 2344 |
+
Log(usize),
|
| 2345 |
+
Userinfo(usize),
|
| 2346 |
+
Format,
|
| 2347 |
+
Send { recipient: i32, text: Vec<u8> },
|
| 2348 |
+
Join { slot: usize, choice: i32 },
|
| 2349 |
+
Rank,
|
| 2350 |
+
}
|
| 2351 |
+
#[derive(Clone, Debug, PartialEq, Eq)]
|
| 2352 |
+
pub struct ConnectTail {
|
| 2353 |
+
pub accepted: bool,
|
| 2354 |
+
pub message: Option<Vec<u8>>,
|
| 2355 |
+
pub events: Vec<ConnectTailEvent>,
|
| 2356 |
+
pub ring_advance: u32,
|
| 2357 |
+
}
|
| 2358 |
+
/// Original173979 suffix with supplied bot/userinfo answers and captured jobs.
|
| 2359 |
+
/// Bot rejection preserves mutations that precede that return in the source.
|
| 2360 |
+
pub fn finish_connect(
|
| 2361 |
+
client: &mut [u8],
|
| 2362 |
+
entity: &mut ConnectTailEntity,
|
| 2363 |
+
input: ConnectTailInput<'_>,
|
| 2364 |
+
) -> Result<ConnectTail, Error> {
|
| 2365 |
+
if client.len() != CLIENT_BYTES || input.slot >= 64 || cbytes(input.name).len() >= 128 {
|
| 2366 |
+
return Err(Error::InvalidInput);
|
| 2367 |
+
}
|
| 2368 |
+
let mut bytes = client.to_vec();
|
| 2369 |
+
let mut e = entity.clone();
|
| 2370 |
+
let mut out = ConnectTail {
|
| 2371 |
+
accepted: true,
|
| 2372 |
+
message: None,
|
| 2373 |
+
events: vec![ConnectTailEvent::Classify(input.mode)],
|
| 2374 |
+
ring_advance: 0,
|
| 2375 |
+
};
|
| 2376 |
+
let mut team = raw_word(&bytes, 3096);
|
| 2377 |
+
team = if crate::intermission::team_mode(input.mode) {
|
| 2378 |
+
if team == 0 { 3 } else { team }
|
| 2379 |
+
} else {
|
| 2380 |
+
if team == 3 { 3 } else { 0 }
|
| 2381 |
+
};
|
| 2382 |
+
raw_store(&mut bytes, 3096, team);
|
| 2383 |
+
if input.bot != 0 {
|
| 2384 |
+
e.flags |= 8;
|
| 2385 |
+
e.in_use = 1;
|
| 2386 |
+
out.events.push(ConnectTailEvent::Bot {
|
| 2387 |
+
slot: input.slot,
|
| 2388 |
+
restart: input.first_time == 0,
|
| 2389 |
+
});
|
| 2390 |
+
if input.bot_answer == 0 {
|
| 2391 |
+
out.accepted = false;
|
| 2392 |
+
out.message = Some(b"BotConnectfailed".to_vec());
|
| 2393 |
+
client.copy_from_slice(&bytes);
|
| 2394 |
+
*entity = e;
|
| 2395 |
+
return Ok(out);
|
| 2396 |
+
}
|
| 2397 |
+
}
|
| 2398 |
+
if input.first_time != 0 {
|
| 2399 |
+
bytes[668..696].fill(0);
|
| 2400 |
+
raw_store(
|
| 2401 |
+
&mut bytes,
|
| 2402 |
+
3140,
|
| 2403 |
+
input.clock.wrapping_add(input.seconds.wrapping_mul(1000)) as u32,
|
| 2404 |
+
);
|
| 2405 |
+
out.events.push(ConnectTailEvent::Zero {
|
| 2406 |
+
offset: 668,
|
| 2407 |
+
length: 28,
|
| 2408 |
+
});
|
| 2409 |
+
}
|
| 2410 |
+
out.events.push(ConnectTailEvent::Log(input.slot));
|
| 2411 |
+
if input.first_time != 0 {
|
| 2412 |
+
raw_store(&mut bytes, 3588, 0);
|
| 2413 |
+
bytes[3204..3332].fill(0);
|
| 2414 |
+
bytes[3332..3588].fill(0);
|
| 2415 |
+
out.events.extend([
|
| 2416 |
+
ConnectTailEvent::Zero {
|
| 2417 |
+
offset: 3204,
|
| 2418 |
+
length: 128,
|
| 2419 |
+
},
|
| 2420 |
+
ConnectTailEvent::Zero {
|
| 2421 |
+
offset: 3332,
|
| 2422 |
+
length: 256,
|
| 2423 |
+
},
|
| 2424 |
+
]);
|
| 2425 |
+
}
|
| 2426 |
+
out.events.push(ConnectTailEvent::Userinfo(input.slot));
|
| 2427 |
+
if let Some(team) = input.after_team {
|
| 2428 |
+
raw_store(&mut bytes, 3096, team as u32);
|
| 2429 |
+
}
|
| 2430 |
+
let name = cbytes(input.name);
|
| 2431 |
+
bytes[504..504 + name.len()].copy_from_slice(name);
|
| 2432 |
+
bytes[504 + name.len()] = 0;
|
| 2433 |
+
if input.first_time != 0 {
|
| 2434 |
+
let text = crate::qvm_format::render(
|
| 2435 |
+
b"print \"%s^7 connected\n\"",
|
| 2436 |
+
&[crate::qvm_format::Argument::Bytes(Some(name))],
|
| 2437 |
+
)
|
| 2438 |
+
.map_err(|_| Error::InvalidInput)?;
|
| 2439 |
+
let text = cbytes(&text).to_vec();
|
| 2440 |
+
out.events.extend([
|
| 2441 |
+
ConnectTailEvent::Format,
|
| 2442 |
+
ConnectTailEvent::Send {
|
| 2443 |
+
recipient: -1,
|
| 2444 |
+
text,
|
| 2445 |
+
},
|
| 2446 |
+
]);
|
| 2447 |
+
out.ring_advance = 1;
|
| 2448 |
+
}
|
| 2449 |
+
out.events.push(ConnectTailEvent::Classify(input.mode));
|
| 2450 |
+
if crate::intermission::team_mode(input.mode) && raw_word(&bytes, 3096) != 3 {
|
| 2451 |
+
out.events.push(ConnectTailEvent::Join {
|
| 2452 |
+
slot: input.slot,
|
| 2453 |
+
choice: -1,
|
| 2454 |
+
});
|
| 2455 |
+
}
|
| 2456 |
+
out.events.push(ConnectTailEvent::Rank);
|
| 2457 |
+
client.copy_from_slice(&bytes);
|
| 2458 |
+
*entity = e;
|
| 2459 |
+
Ok(out)
|
| 2460 |
+
}
|
| 2461 |
+
#[cfg(test)]
|
| 2462 |
+
mod connect_tail_tests {
|
| 2463 |
+
use super::*;
|
| 2464 |
+
#[test]
|
| 2465 |
+
fn bot_reject_keeps_prior_flags_and_team_change() {
|
| 2466 |
+
let mut raw = vec![0; CLIENT_BYTES];
|
| 2467 |
+
let mut e = ConnectTailEntity {
|
| 2468 |
+
flags: 32,
|
| 2469 |
+
in_use: 0,
|
| 2470 |
+
};
|
| 2471 |
+
let out = finish_connect(
|
| 2472 |
+
&mut raw,
|
| 2473 |
+
&mut e,
|
| 2474 |
+
ConnectTailInput {
|
| 2475 |
+
mode: 3,
|
| 2476 |
+
slot: 0,
|
| 2477 |
+
first_time: 1,
|
| 2478 |
+
bot: 1,
|
| 2479 |
+
bot_answer: 0,
|
| 2480 |
+
clock: 100,
|
| 2481 |
+
seconds: 0,
|
| 2482 |
+
after_team: Some(2),
|
| 2483 |
+
name: b"new",
|
| 2484 |
+
},
|
| 2485 |
+
)
|
| 2486 |
+
.unwrap();
|
| 2487 |
+
assert!(!out.accepted);
|
| 2488 |
+
assert_eq!(raw_word(&raw, 3096), 3);
|
| 2489 |
+
assert_eq!(e.flags, 40);
|
| 2490 |
+
assert_eq!(out.events.len(), 2);
|
| 2491 |
+
}
|
| 2492 |
+
#[test]
|
| 2493 |
+
fn first_time_zero_seconds_has_no_initializer_default() {
|
| 2494 |
+
let mut raw = vec![0; CLIENT_BYTES];
|
| 2495 |
+
let mut e = ConnectTailEntity {
|
| 2496 |
+
flags: 0,
|
| 2497 |
+
in_use: 0,
|
| 2498 |
+
};
|
| 2499 |
+
finish_connect(
|
| 2500 |
+
&mut raw,
|
| 2501 |
+
&mut e,
|
| 2502 |
+
ConnectTailInput {
|
| 2503 |
+
mode: 0,
|
| 2504 |
+
slot: 0,
|
| 2505 |
+
first_time: 1,
|
| 2506 |
+
bot: 0,
|
| 2507 |
+
bot_answer: 1,
|
| 2508 |
+
clock: 100,
|
| 2509 |
+
seconds: 0,
|
| 2510 |
+
after_team: None,
|
| 2511 |
+
name: b"new",
|
| 2512 |
+
},
|
| 2513 |
+
)
|
| 2514 |
+
.unwrap();
|
| 2515 |
+
assert_eq!(raw_word(&raw, 3140), 100);
|
| 2516 |
+
}
|
| 2517 |
+
}
|
docs/FINDINGS.md
CHANGED
|
@@ -1760,3 +1760,6 @@ RE-813–822 replaces the scoreboard decimal assumption with actual37914 executi
|
|
| 1760 |
|
| 1761 |
|
| 1762 |
**RE-920:** 144 actual173979 suffix cases execute original mode classification and broadcast formatting with explicit bot/userinfo answers. Team normalization precedes bot flags/admission; failed bots return after those mutations. First-time paths clear selected ranges and overwrite timeout without the initializer60-second default. Updated userinfo/name precedes broadcast and a second team decision; ranking is requested last. Bot, userinfo, join, ranking, logs/network and native memset remain supplied/captured/modeled boundaries. [Evidence](research/QVM_CONNECT_TAIL.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1760 |
|
| 1761 |
|
| 1762 |
**RE-920:** 144 actual173979 suffix cases execute original mode classification and broadcast formatting with explicit bot/userinfo answers. Team normalization precedes bot flags/admission; failed bots return after those mutations. First-time paths clear selected ranges and overwrite timeout without the initializer60-second default. Updated userinfo/name precedes broadcast and a second team decision; ranking is requested last. Bot, userinfo, join, ranking, logs/network and native memset remain supplied/captured/modeled boundaries. [Evidence](research/QVM_CONNECT_TAIL.md).
|
| 1763 |
+
|
| 1764 |
+
|
| 1765 |
+
**RE-921:** Rust matches144 original173979 suffix cases with ordered bot admission, first-time clears, timeout wrapping, supplied userinfo changes, original broadcast bytes, team callback and ranking intentions. Bot rejection retains prior team/flag mutations. Native callbacks/logging/network/ranking and real caller acceptance remain open. [Evidence](research/RUST_CONNECT_TAIL.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -1800,3 +1800,6 @@ RE-919: Rust matches160 original173979 admission prefixes, preserving bot bypass
|
|
| 1800 |
|
| 1801 |
|
| 1802 |
RE-920: 144 actual173979 suffix cases execute original mode classification and broadcast formatting with explicit bot/userinfo answers. Team normalization precedes bot flags/admission; failed bots return after those mutations. First-time paths clear selected ranges and overwrite timeout without the initializer60-second default. Updated userinfo/name precedes broadcast and a second team decision; ranking is requested last. Bot, userinfo, join, ranking, logs/network and native memset remain supplied/captured/modeled boundaries. Full runtime completion remains open. [Evidence](research/QVM_CONNECT_TAIL.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1800 |
|
| 1801 |
|
| 1802 |
RE-920: 144 actual173979 suffix cases execute original mode classification and broadcast formatting with explicit bot/userinfo answers. Team normalization precedes bot flags/admission; failed bots return after those mutations. First-time paths clear selected ranges and overwrite timeout without the initializer60-second default. Updated userinfo/name precedes broadcast and a second team decision; ranking is requested last. Bot, userinfo, join, ranking, logs/network and native memset remain supplied/captured/modeled boundaries. Full runtime completion remains open. [Evidence](research/QVM_CONNECT_TAIL.md).
|
| 1803 |
+
|
| 1804 |
+
|
| 1805 |
+
RE-921: Rust matches144 original173979 suffix cases with ordered bot admission, first-time clears, timeout wrapping, supplied userinfo changes, original broadcast bytes, team callback and ranking intentions. Bot rejection retains prior team/flag mutations. Native callbacks/logging/network/ranking and real caller acceptance remain open. Full runtime completion remains open. [Evidence](research/RUST_CONNECT_TAIL.md).
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -2325,3 +2325,8 @@ Rust matches160 original173979 admission prefixes, preserving bot bypass, addres
|
|
| 2325 |
## 2026-10-28 — RE-920 Original post-session connect normalization and callbacks
|
| 2326 |
|
| 2327 |
144 actual173979 suffix cases execute original mode classification and broadcast formatting with explicit bot/userinfo answers. Team normalization precedes bot flags/admission; failed bots return after those mutations. First-time paths clear selected ranges and overwrite timeout without the initializer60-second default. Updated userinfo/name precedes broadcast and a second team decision; ranking is requested last. Bot, userinfo, join, ranking, logs/network and native memset remain supplied/captured/modeled boundaries. Focused `cargo xtask test-group qvm-connect-tail` passes (4 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2325 |
## 2026-10-28 — RE-920 Original post-session connect normalization and callbacks
|
| 2326 |
|
| 2327 |
144 actual173979 suffix cases execute original mode classification and broadcast formatting with explicit bot/userinfo answers. Team normalization precedes bot flags/admission; failed bots return after those mutations. First-time paths clear selected ranges and overwrite timeout without the initializer60-second default. Updated userinfo/name precedes broadcast and a second team decision; ranking is requested last. Bot, userinfo, join, ranking, logs/network and native memset remain supplied/captured/modeled boundaries. Focused `cargo xtask test-group qvm-connect-tail` passes (4 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
|
| 2328 |
+
|
| 2329 |
+
|
| 2330 |
+
## 2026-10-28 — RE-921 Rust post-session connect callbacks and normalization
|
| 2331 |
+
|
| 2332 |
+
Rust matches144 original173979 suffix cases with ordered bot admission, first-time clears, timeout wrapping, supplied userinfo changes, original broadcast bytes, team callback and ranking intentions. Bot rejection retains prior team/flag mutations. Native callbacks/logging/network/ranking and real caller acceptance remain open. Focused `cargo xtask test-group rust-connect-tail` passes (9 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -1837,3 +1837,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 1837 |
|
| 1838 |
|
| 1839 |
- [x] RE-920 Original post-session connect normalization and callbacks ([evidence](research/QVM_CONNECT_TAIL.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 1837 |
|
| 1838 |
|
| 1839 |
- [x] RE-920 Original post-session connect normalization and callbacks ([evidence](research/QVM_CONNECT_TAIL.md)).
|
| 1840 |
+
|
| 1841 |
+
|
| 1842 |
+
- [x] RE-921 Rust post-session connect callbacks and normalization ([evidence](research/RUST_CONNECT_TAIL.md)).
|
docs/research/README.md
CHANGED
|
@@ -1372,3 +1372,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 1372 |
|
| 1373 |
|
| 1374 |
[RE-920: Original post-session connect normalization and callbacks](QVM_CONNECT_TAIL.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1372 |
|
| 1373 |
|
| 1374 |
[RE-920: Original post-session connect normalization and callbacks](QVM_CONNECT_TAIL.md).
|
| 1375 |
+
|
| 1376 |
+
|
| 1377 |
+
[RE-921: Rust post-session connect callbacks and normalization](RUST_CONNECT_TAIL.md).
|
docs/research/RUST_CONNECT_TAIL.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Rust post-session connect callbacks and normalization
|
| 2 |
+
|
| 3 |
+
RE-921. Reproduce with `python3 scripts/analyze_rust_connect_tail.py`; focused validation: `cargo xtask test-group rust-connect-tail`. [Machine report](rust-connect-tail.json).
|
| 4 |
+
|
| 5 |
+
Rust matches144 original173979 suffix cases with ordered bot admission, first-time clears, timeout wrapping, supplied userinfo changes, original broadcast bytes, team callback and ranking intentions. Bot rejection retains prior team/flag mutations. Native callbacks/logging/network/ranking and real caller acceptance remain open.
|
| 6 |
+
|
| 7 |
+
Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
|
docs/research/rust-connect-tail.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": 175,
|
| 2989 |
"source_report": "docs/research/qvm-health-impulse.json",
|
| 2990 |
"source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
|
| 2991 |
}
|
scripts/analyze_rust_connect_tail.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import hashlib,json
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from rust_native_policy_support import binary,result,invoke
|
| 5 |
+
from analyze_qvm import write_json
|
| 6 |
+
def encode(s):
|
| 7 |
+
a=s['input'];return [a[k] for k in ['mode','team','bot','first','answer','changed','now','seconds']]
|
| 8 |
+
def project(s):return {k:s[k] for k in ['accepted','message','events','words','entity_flags','in_use','ring']}
|
| 9 |
+
|
| 10 |
+
def main():
|
| 11 |
+
b=binary();p=Path('docs/research/qvm-connect-tail.json');data=p.read_bytes();rows=[]
|
| 12 |
+
for s in json.loads(data)['probes']:rows.append({'source':project(s),'rust':result(b,'connect-tail',encode(s))})
|
| 13 |
+
write_json(Path('docs/research/rust-connect-tail.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'probes':rows,'limits':'Rust matches144 original173979 suffix cases with ordered bot admission, first-time clears, timeout wrapping, supplied userinfo changes, original broadcast bytes, team callback and ranking intentions. Bot rejection retains prior team/flag mutations. Native callbacks/logging/network/ranking and real caller acceptance remain open.'})
|
| 14 |
+
if __name__=='__main__':main()
|
scripts/test_rust_connect_tail.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib,json,unittest
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
class PortTests(unittest.TestCase):
|
| 4 |
+
def test_all_original_cases(self):
|
| 5 |
+
r=json.loads(Path('docs/research/rust-connect-tail.json').read_text());src=json.loads(Path(r['source_report']).read_text());self.assertEqual(len(r['probes']),len(src['probes']));self.assertGreater(len(r['probes']),20)
|
| 6 |
+
for p in r['probes']:self.assertEqual(p['rust'],p['source'])
|
| 7 |
+
def test_source_identity(self):
|
| 8 |
+
r=json.loads(Path('docs/research/rust-connect-tail.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
|
@@ -2966,3 +2966,18 @@ edge docs/research/qvm-connect-tail.json scripts/test_qvm_connect_tail.py
|
|
| 2966 |
group qvm-connect-tail scripts/analyze_qvm_connect_tail.py
|
| 2967 |
edge scripts/analyze_qvm_connect_tail.py scripts/analyze_qvm_resumer_audit.py
|
| 2968 |
edge scripts/qvm_connect_tail_oracle.py scripts/analyze_qvm_resumer_audit.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2966 |
group qvm-connect-tail scripts/analyze_qvm_connect_tail.py
|
| 2967 |
edge scripts/analyze_qvm_connect_tail.py scripts/analyze_qvm_resumer_audit.py
|
| 2968 |
edge scripts/qvm_connect_tail_oracle.py scripts/analyze_qvm_resumer_audit.py
|
| 2969 |
+
edge Cargo.toml scripts/analyze_rust_connect_tail.py
|
| 2970 |
+
edge Cargo.lock scripts/analyze_rust_connect_tail.py
|
| 2971 |
+
edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_connect_tail.py
|
| 2972 |
+
edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_connect_tail.py
|
| 2973 |
+
edge crates/urt-gameplay/src/native_policy.rs scripts/analyze_rust_connect_tail.py
|
| 2974 |
+
edge crates/urt-gameplay/src/bin/urt-native-policy.rs scripts/analyze_rust_connect_tail.py
|
| 2975 |
+
edge docs/research/qvm-connect-tail.json scripts/analyze_rust_connect_tail.py
|
| 2976 |
+
group rust-connect-tail scripts/analyze_rust_health_impulse.py
|
| 2977 |
+
group gameplay scripts/analyze_rust_connect_tail.py
|
| 2978 |
+
edge crates/urt-gameplay/src/intermission.rs scripts/analyze_rust_connect_tail.py
|
| 2979 |
+
edge crates/urt-gameplay/src/qvm_format.rs scripts/analyze_rust_connect_tail.py
|
| 2980 |
+
report scripts/analyze_rust_connect_tail.py docs/research/rust-connect-tail.json
|
| 2981 |
+
edge docs/research/rust-connect-tail.json scripts/test_rust_connect_tail.py
|
| 2982 |
+
group rust-connect-tail scripts/analyze_rust_connect_tail.py
|
| 2983 |
+
edge scripts/analyze_rust_connect_tail.py scripts/analyze_qvm_resumer_audit.py
|