leakhunt/bin/bench_roundtrip.ahk
acbot 57b5e43d0e Initial commit — leak-hunt project complete
Five bugs identified and patched in retail Asheron's Call client:
- v3b: palette refcount over-increment (3-byte NOP at two sites)
- v5: RenderSurface PurgeResource no-op stub (vtable slot 2 thunk)
- v11: two dangling-pointer crash guards (NULL-check + reorder)
- v14: CEnvCell::Destroy ClipPlaneList leak (18-byte JMP to cleanup thunk)
- v22: unpacker stale-pointer SEH guard (whole-function __try/__except)

All five ship in leakfix.dll (117 KB, SHA d282f23c…) which is loaded
by acclient.exe at process start via PE import table patching by
tools/install_leakfix.py.

Controlled 15-client fleet soak: unpatched control died at 26h with
palette exhaustion; all 14 patched clients survived past that point
and reached ≥5-day uptime.

Residual ~15 MB/h growth traced to d3d9.dll's internal slab allocator
(260KB surface backing buffers retained after Release). See REPORT.md
§10 for the full investigation; conclusion is that it's unfixable from
outside d3d9.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 21:07:58 +02:00

71 lines
2.1 KiB
AutoHotkey

; bench_roundtrip.ahk
; Phase 0 step 4 — drive the AC client through char-select → in-world → clean quit,
; without a human watching. Logs to artifacts/phase0/ahk_roundtrip.log.
;
; Run with: AutoHotkey64.exe bench_roundtrip.ahk
#Requires AutoHotkey v2.0
#SingleInstance Force
WinTitle := "Asheron's Call"
LogFile := "C:\Users\acbot\leakhunt\artifacts\phase0\ahk_roundtrip.log"
Log(msg) {
global LogFile
FileAppend Format("[{1}] {2}`n", FormatTime(A_Now, "yyyy-MM-dd HH:mm:ss"), msg), LogFile
}
Log("script start")
if not WinWait(WinTitle, , 30) {
Log("FATAL: AC window not found within 30s")
ExitApp 1
}
Log("AC window found")
WinActivate WinTitle
Sleep 1500
if not WinActive(WinTitle) {
Log("WARN: WinActivate did not bring AC to foreground; trying ControlSend fallback")
}
; Phase A: if at char-select, Enter selects the default (only) character and enters world.
; If we're already in-world (memory >800 MB suggested by the supervisor probe), Enter opens
; chat which we'll close immediately.
SendInput "{Enter}"
Log("sent Enter #1 (char-select confirm or chat-open)")
Sleep 2000
; Phase B: send Escape to dismiss any modal/chat that may have opened. Harmless if no modal.
SendInput "{Esc}"
Log("sent Escape (dismiss modal/chat)")
Sleep 800
; Phase C: small movement step to satisfy "walk one step" requirement (Phase 0 §4).
; W is the AC default forward-walk bind in most layouts. Hold 250 ms.
SendInput "{w down}"
Sleep 250
SendInput "{w up}"
Log("sent W pulse (walk one step)")
Sleep 1000
; Phase D: clean quit. AC supports @quit chat command to log out to character select.
; Then we send /quit again from char-select to terminate the client process gracefully.
SendInput "{Enter}" ; open chat
Sleep 200
SendInput "@quit"
Sleep 200
SendInput "{Enter}"
Log("sent @quit (logout-to-char-select)")
Sleep 4000
; If still alive at char-select: ask the launcher menu to exit the client.
; AC's exit-to-desktop on char-select is typically Escape -> Yes-to-exit confirmation.
SendInput "{Esc}"
Sleep 600
SendInput "{Enter}" ; default focus on "Quit" or "Yes"
Log("sent Escape + Enter (exit from char-select)")
Sleep 1500
Log("script end")
ExitApp 0