Appearance
aowlspt_bridge.h
Source: abi/aowlspt_bridge.h — 544 lines, 18 file-scope functions.
What this header owns
Reproduced verbatim from the header's own banner comment — these notes are frequently the only written record of why the subsystem is shaped the way it is.
text
aowlspt_bridge.h -- getting host-side work onto Unity's main thread.
## The problem this closes
`il2cpp_runtime_invoke` from the host's own injected thread crashes this
client. IL2CPP/Unity managed code -- the GC, the managed heap, every Unity
object -- is affine to the Unity **main thread** (the one that runs the
player loop). A managed call, a box, or a field read issued from a foreign
thread faults inside the runtime. The host boots on a thread of its own, so
everything it wants to do to a live game object is on the wrong thread.
The fix already lives in `host/Aowlspt.Host.Il2Cpp/aowlhost.nim`: detour a
method Unity calls once per frame on its main thread and drain the
`invoke_main` queue from inside the detour (`bindMainDrain` / `mainDrain`).
A closure pushed from any thread then runs, on the Unity thread, the next
frame. What was missing was a per-frame method the host could actually get a
usable code pointer for.
## Why the by-name path was not enough, and what this adds
`bindMainDrain`'s by-name path resolves a method through the runtime
(`il2cpp_class_from_name` -> `il2cpp_class_get_method_from_name` -> read
`MethodInfo.methodPointer`). On this build that returned nothing usable for
every candidate -- but NOT because the metadata is protected. Two real
reasons: one candidate, `EFT.MainApplication`, does not exist post-1.0 (the
root application MonoBehaviour is **`EFT.TarkovApplication`**); and, decisive,
`bindMainDrain` runs on the host's own boot thread, and reading IL2CPP
class/method metadata off the Unity main thread returns bogus pointers and
faults (see the repo memory `project_post1_menu_boot`). So `findClass` /
`findMethod` handed back garbage and `methodPointer` read garbage -> null.
That is a wrong-thread artifact, not proof of protected metadata.
The decrypted metadata's per-module `methodPointers` tables are in fact
INTACT and reliable on this build. Proof: this file's resolver reproduces all
five BE-bypass RVAs in `aowlspt_beclient.h` byte-for-byte (they live in the
`Assembly-CSharp-firstpass.dll` module, table base 0x186BF1380 -- an EARLIER
misread used `Assembly-CSharp.dll`'s table for every type and so missed them,
which is where the false "the table is null for BE" note used to be), and it
reproduces every VA in `docs/SETTINGS.md` (SettingsScreen.Show 0x171FA00,
OpenGroup 0x1720C80, ...). Two independent ground truths, exact.
So resolution is **static**, offline, the same mechanism as the BE patches:
type -> its image (from the metadata images table) -> that image's
`Il2CppCodeGenModule.methodPointers` -> `[token_rid - 1]` -> code RVA, then
byte-verify the prologue against `GameAssembly.dll` before use. No runtime
metadata read, no off-thread hazard.
## The target
`EFT.TarkovApplication::Update` @ RVA 0x977B10 (imagebase 0x180000000,
build 1.1.0.1.46777, in the `il2cpp` PE section at VA 0x628000+).
It is the root application MonoBehaviour's per-frame `Update`. It is the best
of the candidates on all four counts a drain target needs:
- once per frame,
- on the Unity main thread only (a MonoBehaviour `Update` is a player-loop
callback and runs nowhere else),
- alive for the whole session, menu to desktop (the application root
outlives every scene, unlike `GameWorld`, which exists only in a raid),
- and its compiled prologue relocates cleanly -- `push rbx ; sub rsp,0x20 ;
mov rbx,[rcx+0x110] ; test rbx,rbx`, sixteen bytes with no RIP-relative
operand and no relative branch, which is exactly what the detour engine's
length decoder in `aowlspt_detour.h` needs to steal a whole number of
instructions for the trampoline.
How the RVA was found: the decrypted `global-metadata.dat` gives the method's
token RID within `Assembly-CSharp`, and the module's `Il2CppCodeGenModule.
methodPointers` table in `GameAssembly.dll`'s `.data` maps RID -> code RVA,
exactly as Il2CppDumper does. `EFT.TarkovApplication` is type index 7933; its
`Update` resolves to 0x977B10 and its `LateUpdate` to the adjacent 0x977C80,
the tight monotonic clustering a real compiler emits for one type's methods.
A secondary, raid-only target -- `EFT.GameWorldUnityTickListener::Update` @
RVA 0x251C6C0 -- is included as a fallback: it is present only inside a raid,
but a raid is where a mod most wants the main thread, and binding it late
beats not binding at all if the primary's prologue ever fails its guard.
## Safe by default
Every target checks the bytes it expects before it is handed out. On any
other build the prologue will not match, and this returns NULL -- the host
then falls through to its by-name candidates and, failing those, to the
existing host-thread behaviour, which is the documented fallback. A wrong
offset is a missed bind, never a corrupted game. Nothing here writes to the
game; it only *locates and verifies* a function pointer. The detour itself is
installed by the host through the same `aowl_hook_*` engine and slot table a
mod's patch uses, so it inherits that engine's thread-park write safety.
RVAs are for imagebase 0x180000000 and build 1.1.0.1.46777.Constants
AOWLSPT_BRIDGE_HAOWL_BRIDGE_RENDER_TARGET_COUNTAOWL_BRIDGE_SETTINGSTAB_TARGET_COUNTAOWL_BRIDGE_SETTINGSTICK_TARGET_COUNTAOWL_BRIDGE_SETTINGS_TARGET_COUNTAOWL_BRIDGE_TARGET_COUNTAOWL_IL2CPP_SEC_RVAAOWL_IL2CPP_SEC_SIZE
Types
struct AowlBridgeTarget
Functions
| Signature | Line |
|---|---|
void aowl_bridge_render_target_at(int32_t i) | 202 |
char aowl_bridge_render_target_name(int32_t i) | 229 |
int32_t aowl_bridge_render_target_count(void) | 234 |
void aowl_bridge_settings_target_at(int32_t i) | 261 |
char aowl_bridge_settings_target_name(int32_t i) | 282 |
int32_t aowl_bridge_settings_target_count(void) | 286 |
void aowl_bridge_settingstab_target_at(int32_t i) | 356 |
char aowl_bridge_settingstab_target_name(int32_t i) | 377 |
int32_t aowl_bridge_settingstab_target_count(void) | 381 |
void aowl_bridge_settingstick_target_at(int32_t i) | 429 |
char aowl_bridge_settingstick_target_name(int32_t i) | 450 |
int32_t aowl_bridge_settingstick_target_count(void) | 454 |
void aowl_bridge_target_at(int32_t i) | 465 |
int32_t aowl_bridge_target_perframe(int32_t i) | 498 |
char aowl_bridge_target_name(int32_t i) | 503 |
int32_t aowl_bridge_target_count(void) | 508 |
int32_t aowl_in_il2cpp_section(void* p) | 520 |
int64_t aowl_il2cpp_rva_of(void* p) | 536 |

