Files
benilla-octo/crates/benilla-protocol/examples/park_probe.rs
T
SamandClaude Opus 5.5 8d9f007415 comments: protocol, the auth code, the live probes and their examples
1,071 comment lines become 573; the code is unchanged. The world
probe's field docs are its --help text, so the help is shorter now and
matches what each flag runs: --quest takes quest 783 from Willem to
McBride, --quest-item needs the starter item to survive, --giverstatus
queries once, --spells makes three casts. Also corrected:
- the aura-duration packet has a SessionEvent;
- the SRP xor hash is SHA1(N) XOR SHA1(g), not a hash of it;
- the cooldown probe picks Tri, else the first character.

benilla sending a stored integrity hash instead of hashing the install
is now marked as a deviation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 20:09:30 +07:00

63 lines
2.2 KiB
Rust

//! Live probe: the character-select park behaviours the glue screens rely on. A logout ends in
//! `SMSG_LOGOUT_COMPLETE` and a reconnect re-serves the roster; a socket parked at character
//! select for 130 s still logs in, since vmangos does not kick a quiet authenticated socket.
//!
//! Needs the local vmangos and account `two`/`ptwo`.
use std::time::Duration;
use benilla_protocol::{logon, WorldSession, WORLD_PORT};
fn connect(user: &str, pass: &str) -> anyhow::Result<WorldSession> {
let l = logon("localhost", user, pass)?;
let addr = l
.realms
.first()
.map(|r| r.address.clone())
.unwrap_or_else(|| format!("localhost:{WORLD_PORT}"));
WorldSession::connect(&addr, user, l.session_key)
}
fn main() -> anyhow::Result<()> {
let (user, pass) = ("two", "ptwo");
// ── 1 · logout round-trip ──
let mut s = connect(user, pass)?;
let chars = s.char_enum()?;
println!(
"roster: {:?}",
chars.iter().map(|c| &c.name).collect::<Vec<_>>()
);
let c = &chars[0];
s.player_login(c.guid)?;
s.set_active_mover(c.guid)?;
println!("logged in as {} — requesting logout in 2s", c.name);
std::thread::sleep(Duration::from_secs(2));
s.set_read_timeout(Some(Duration::from_secs(1)))?;
s.logout(Duration::from_secs(25))?;
println!("PASS: SMSG_LOGOUT_COMPLETE received");
drop(s);
// A fresh connection re-serves the roster, as the app's relist does.
let mut s = connect(user, pass)?;
let n = s.char_enum()?.len();
println!("PASS: post-logout reconnect roster has {n} chars");
drop(s);
// ── 2 · idle park ──
let mut s = connect(user, pass)?;
let chars = s.char_enum()?;
println!("parking at character select for 130s…");
std::thread::sleep(Duration::from_secs(130));
match s.player_login(chars[0].guid).and_then(|()| {
s.set_active_mover(chars[0].guid)?;
// Any post-login packet shows the world streams.
s.set_read_timeout(Some(Duration::from_secs(5)))?;
s.recv().map(|p| p.name())
}) {
Ok(pkt) => println!("PASS: idle-parked login works (first packet: {pkt})"),
Err(e) => println!("KICKED: idle park failed login: {e:#} (self-heals via relist)"),
}
Ok(())
}