Record where the runtime chain actually stops on Unreal - #88
Merged
Conversation
First real-game test of the chain, on Father's Day. The transport half works: the DLL connects to the Rust server inside a shipping title, and the game stays healthy — SendMessageTimeout answers, 103s CPU, 608 MB. That confirms the overlapped-I/O fix on something other than the test app, which matters, because that bug froze the process instantly. The other half does not. Zero requests reach the server, because the Unreal L1 source refuses to hook FText::ToString when its byte pattern is ambiguous — four matches here — and declines rather than hooking the wrong function. The GDI sources stay active but see nothing: UE draws through Slate/Direct3D, not ExtTextOutW. The missing link is symbol resolution, not IPC. The trap is the log line "connesso a GameStringer via IPC", which reads like success and is only half the chain. The number that decides is how many requests reach the server; at zero, the problem is upstream of the IPC and staring at the pipe will never find it. Carries the corollary for #87: hooking the fallback to the "Unreal without .locres" dead end is right in principle, but while that pattern stays ambiguous the runtime path has no text to translate on UE, so it would offer a road that leads nowhere yet. RPG_RT and GDI games are different — that source works and has been seen working. Second entry, on the Paks folder: every UE game has at least two, and the first one found is usually the decoy — Engine/Programs/CrashReportClient/Content/Paks. I measured that one and concluded REANIMAL had no localization: 45 MB of crash reporter against 15.6 GB of game. The control that catches it is The Skin Stapler, which must come back positive at 1679 entries; while a method says otherwise, the method is what is broken. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rouges78
added a commit
that referenced
this pull request
Aug 21, 2026
…us (#89) A symbol is deterministic: it is either there and correct, or absent. That is strictly better than a byte signature, so it now runs first, with the pattern scan as the fallback. ResolveFTextToStringBySymbol looks for ?ToString@FText@@QEBAAEBVFString@@xz across every loaded module. It does not fix Father's Day, and it cannot: symbol resolution needs the engine in DLLs, and a monolithic Shipping build has no UE symbols at all. Measured, not assumed — 312 exports in the exe and every one a driver hint (NvOptimusEnablement, ags*), the PDB named in the header but not shipped, turbojpeg.dll the only DLL beside it, and 142 modules interrogated at runtime with zero hits. The value is for non-monolithic and editor builds; commercial games still fall through to the pattern. The module count is logged on purpose. Without it a zero cannot separate "looked everywhere, not there" from "the snapshot failed and I looked at nothing" — the same trap as the "connesso via IPC" line recorded in #88. The check that confirms it is comparing against Modules.Count: 142 against 142. The four ambiguous candidates are measured and written down: four distinct functions sharing a prologue, diverging at byte 13, inside the 17 the signature covers. Lengthening it would mean retuning against the four games where it is already unique, with no ground truth to say which candidate is even correct. Refusing to guess remains right: a wrong hook in a shipping game is a crash or silent heap corruption. The entry names the promising direction instead — anchoring on a string reference rather than a prologue. Verified on the real game: symbol path exercised, pattern still correctly refuses, GDI fallback activates, game responsive, no regression. Both architectures rebuilt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First real-game test of the runtime chain, on Father's Day (UE, Steam). Two entries in the log, one of them a correction to the priorities.
The transport works. The text source does not.
The DLL connects to the Rust server inside a shipping title, and the game stays healthy —
SendMessageTimeout(WM_NULL, 3000ms)answers, 103 s CPU, 608 MB. That confirms the overlapped-I/O fix from #85 on something other than the test app, which matters: that bug froze the process instantly.But zero requests reach the server:
The Unreal L1 source declines to hook
FText::ToStringwhen its byte pattern is ambiguous, and refusing is the right call — a wrong hook in a shipping game means a crash. The GDI sources stay active but see nothing, because UE draws through Slate/Direct3D. The missing link is symbol resolution, not IPC.The trap worth recording:
connesso a GameStringer via IPCreads like success and is only half the chain. The number that decides is how many requests reach the server; at zero, the problem is upstream of the IPC, and staring at the pipe will never find it.Corollary for #87
Hooking the runtime fallback to the "Unreal without
.locres" dead end is right in principle, but while that pattern stays ambiguous the runtime path has no text to translate on UE — it would offer a road that leads nowhere yet. RPG_RT and GDI games are different: that source works and has been seen working. The entry says so plainly so the next person does not assume UE is covered.Second entry: the Paks decoy
Surveying the library for a test candidate, I fell into the trap the log's own first entry warns about — in a variant it did not cover. Every UE game has at least two
Paksfolders, and the first one found is usuallyEngine/Programs/CrashReportClient/Content/Paks. I measured that one and concluded REANIMAL shipped no localization: 45 MB of crash reporter against 15.6 GB of game; TerraTech Legion, 46 MB against 5.5 GB.The control that catches it is The Skin Stapler, which must come back positive at 1679 entries. While a method says otherwise, the method is what is broken — and
find_all_paks_dirs()exists precisely so nobody has to rediscover this with an improvisedfind.Docs only — no code changes.
🤖 Generated with Claude Code