Appearance
aowlspt_raidstart.h
Source: abi/aowlspt_raidstart.h — 189 lines, 4 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_raidstart.h -- the TRUE deploy signal for the shared raid-phase latch.
## The bug this exists for
`raidphase.nim` armed its deploy latch on five READABLE signals (GameWorld
cached, MainPlayer Unity-alive, MainPlayer in AllAlivePlayersList,
SessionEndUIScene absent, Camera.main non-null). All five are satisfied at the
END OF SCENE LOAD -- the world is built, the local player is spawned into the
alive list and a camera exists -- which is roughly TEN SECONDS before the
player actually deploys and the raid is on screen. The user reported the ESP
boxes and the map HUD appearing during that window three separate times. The
latch mechanics were correct; the SIGNAL was early.
## What the true signal is, and what was measured
All of the following are OFFLINE measurements on build 1.1.0.1.46777, from
`tools/il2cpp_resolve.py` (methodPointers + prologue bytes from
GameAssembly.dll) and `tools/fldoff.py` (Il2CppMetadataRegistration.
fieldOffsets). Every RVA below was checked for SHAREDNESS -- 28.3% of by-name
lookups on this build land on an RVA with more than one owner, and detouring
one of those fires for every owner.
EFT.GameWorld::OnGameStarted @ 0x2508000 UNIQUE *** NOT HOOKABLE
EFT.GameWorld::Dispose @ 0x2501050 UNIQUE (verified, not bound)
EFT.AbstractGame::get_Status @ 0x8AD140 SHARED x60
EFT.AbstractGame::set_Status @ 0x7C9AD0 SHARED x35
Audio.SpatialSystem.SpatialAudioSystem::AfterGameStarted @ 0x2194770 UNIQUE
EFT.ExfiltrationTimerSoundPlayer::AfterGameStarted @ 0xA4CAF0 UNIQUE
### Why OnGameStarted itself is not hooked
Its compiled prologue is
48 8B 89 30 01 00 00 mov rcx, [rcx+0x130] ; GameWorld.AfterGameStarted
48 85 C9 test rcx, rcx
74 0F je +0xF
-- ten stealable bytes and then a RELATIVE BRANCH at offset 10. The detour
engine needs `AOWL_JMP_SIZE` = 14 (`jmp qword ptr [rip+0]`) and refuses to
relocate a relative branch (`aowl_stolen_len_why` -> -6). The five-byte
`jmp rel32` + island form that would fit is explicitly documented in
aowlspt_detour.h as the road NOT taken. So this address is a correct fact and
an unusable hook, and `abi/aowlspt_botdiag.h` already recorded it as such.
### What is hooked instead, and why it is the SAME instant
The first two instructions of `OnGameStarted` are the proof: the method's
whole job is to load `GameWorld.AfterGameStarted` (+0x130, an `Action`,
confirmed by `fldoff.py fields EFT.GameWorld`) and invoke it. So every
subscriber to that Action runs INSIDE `OnGameStarted`, i.e. at exactly the
true deploy instant, not near it. An exhaustive method-name search of all
31282 types found precisely three non-generated `AfterGameStarted()` bodies:
Audio.SpatialSystem.SpatialAudioSystem::AfterGameStarted @0x2194770 UNIQUE
48 89 5C 24 08 mov [rsp+8], rbx
48 89 74 24 10 mov [rsp+0x10], rsi
57 push rdi
48 83 EC 20 sub rsp, 0x20 -> 15 bytes, whole
instructions, NO branch,
NO rip-relative operand.
EFT.ExfiltrationTimerSoundPlayer::AfterGameStarted @0xA4CAF0 UNIQUE
40 53 push rbx
48 83 EC 20 sub rsp, 0x20
80 3D <disp32> 00 cmp byte [rip+..], 0 (rip-relative -- the engine
RELOCATES disp32; the disp
is in the signature only to
pin the build)
48 8B D9 mov rbx, rcx -> 16 bytes, no branch.
EFT.RaidTimerAnnouncementController::AfterGameStarted @0xA43BC0
*** SHARED with 2 owners -- REJECTED, not listed below. Hooking it
would fire for a method we never identified.
BOTH unique ones are listed and BOTH are bound, because either alone is a
subscription we cannot prove offline: a body existing is not a body
subscribed. Either firing is the same instant, and `raidphase` records which.
If NEITHER fires in a live raid the latch reports INCONCLUSIVE and says so --
it does not quietly fall back to the early proxies.
## AbstractGame.Status -- proven offset, no reachable instance
`<Status>k__BackingField` is at **+0x38**, and that is not taken from metadata
alone: both accessors compile to a single instruction each and agree.
get_Status 8B 41 38 C3 mov eax, [rcx+0x38] ; ret
set_Status 89 51 38 C3 mov [rcx+0x38], edx ; ret
(Which is also exactly why both RVAs are shared 60 and 35 ways -- identical
one-instruction bodies are folded. Neither may be detoured.)
The offset is recorded here so no future session re-derives it. It is NOT read
by the host, because there is no verified path to an `AbstractGame` instance:
the only two non-compiler-generated fields typed `AbstractGame` in the whole
image (`EFT.NonWavesSpawnScenario._game`, `<LateFixedUpdateWorker>d__5.<>4__this`)
are unreachable from anything this host holds, `EFT.TarkovApplication` has no
game field (its base is an instantiated generic singleton whose layout
CLAUDE.md 5 says is not reachable offline), and `EFT.Player` has none either.
Guessing a hop here would be the "offset that reads a plausible number" this
project keeps paying for. That leg stays INCONCLUSIVE and is reported as such.
## Safety
READ-ONLY. Both targets are located by RVA, VirtualQuery'd for committed
executable memory, and memcmp'd against the recorded prologue; NULL on any
mismatch, so a different build gets a missed bind and never a corrupted game.
The detours read no argument and touch no game memory at all -- they set one
host-side flag and a timestamp. Nothing here writes to the game.Constants
AOWLSPT_RAIDSTART_HAOWL_RS_ABSTRACTGAME_STATUSAOWL_RS_RVA_GW_DISPOSEAOWL_RS_RVA_ONGAMESTARTEDAOWL_RS_TARGET_COUNT
Types
struct AowlRaidStartTarget
Functions
| Signature | Line |
|---|---|
int32_t aowl_rs_off_status(void) | 123 |
void aowl_rs_target_at(int32_t i) | 156 |
char aowl_rs_target_name(int32_t i) | 181 |
int32_t aowl_rs_target_count(void) | 186 |

