summaryrefslogtreecommitdiffstats
path: root/CONTEXT.md
blob: b1ec4e714e381c0ddc0c161fa31a326e22a3c2f7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# Context: HL2 Linux Internal Cheat (`hl2hake`)

## What this is

A 32-bit Linux internal for Half-Life 2 (native `hl2_linux`), loaded with
`LD_PRELOAD` through Steam's bundled 32-bit runtime. It started as a port of
the Windows `hake` reference (auto-bhop / auto-ABH / auto-climb) and now also
covers ESP, aimbot, triggerbot and silent aim.

## Build / run

- `./build.sh` -> `libhl2hake.so`
  (`g++ -m32 -O2 -std=c++17 -Wall -Wextra -Werror -fPIC -shared`,
  sources: `hl2h modules cinput netvars menu esp server bones vischeck`, `-ldl`).
- `./play.sh` -> Steam runtime + `hl2.sh -steam -novid -console +map d1_town_01`.
- Logs: `/tmp/hl2hake.log` (`logf`) and stderr. `./play.sh > /tmp/hl2run.log 2>&1`
  captures everything.
- Debug env:
  - `HL2H_DRAW_TEST=1` — red probe rect + text, no menu needed.
  - `HL2H_BONE_DEBUG=1` — white marker at each NPC head hitbox.
  - `HL2H_NO_SERVER=1` — disable server-side health reads.
- Config: `$XDG_CONFIG_HOME/hl2hake/config`, falling back to
  `~/.config/hl2hake/config`. Plain INI, saved on every change.

## Runtime architecture

- Modules from `/proc/self/maps`: `client.so`, `engine.so`, `server.so`.
  PROT_NONE ranges excluded; file-backed bss tails merged.
- Calling convention: cdecl (i386); `this` is the first stack argument.
- Hooks (copy-and-swap vtable, 256 slots):
  - `CHLClient` slot 21 `CreateMove`; slot 35 `FrameStageNotify`.
  - `ISurface` slot 114 `PaintTraverseEx` (primary draw);
    slot 22 `DrawPrintText` fallback. Draws wrapped in
    `PushMakeCurrent`/`PopMakeCurrent`.
- `CInput*` discovered by byte-scanning the `CHLClient::CreateMove` prologue.
  Usercmd CRC-32 is repaired after any cmd edit (`note_modified`).
- Console output via `ConMsg` from `libtier0.so`
  (`_Z6ConMsgPKcz`), not `IVEngineClient::ClientCmd`.

## Interfaces / indices

| Interface | Version | Notes |
|---|---|---|
| Client | `VClient017` | |
| Entity list | `VClientEntityList003` | |
| Engine | `VEngineClient014` | |
| Model info | `VModelInfoClient006` | |
| Engine server | `VEngineServer021` | edicts / listen-server entities |
| Debug overlay | `VDebugOverlay003` | accurate world->screen |
| Engine trace | `EngineTraceClient003` | vischeck / trigger ray |
| Surface | `VGUI_Surface030` | |

Relevant vtable slots (this build):

- Engine: `GetLocalPlayer` 12, `Time` 14, `GetViewAngles` 19, `SetViewAngles` 20.
- Entity list: `GetClientNetworkable` 0, `GetClientEntity` 3,
  `GetHighestEntityIndex` 6.
- `IClientNetworkable`: `GetClientClass` 2. `ClientClass::m_pNetworkName` at +8.
- `IClientUnknown`: `GetClientRenderable` 6.
- `IClientRenderable`: `GetModel` 9, `SetupBones` 16.
- `IVModelInfo`: `GetModel` 2, `GetModelName` 4, `GetStudiomodel` 29.
- `IVEngineServer`: `PEntityOfEntIndex` 19.
  Edict `m_pUnk` at +0xC; `IServerUnknown::GetBaseEntity` 6.
- `IVDebugOverlay`: `ScreenPosition` 9.
- `IEngineTrace`: `TraceRay` 4. `Ray_t` uses 16-byte `VectorAligned`;
  `trace_t`: `fraction` 44, `startsolid` 55, `m_pEnt` 76.

## Netvars / entity data (this build)

| Field | Client offset |
|---|---|
| `m_fFlags` | `0x33C` |
| `m_vecVelocity[0]` | `0xE0` |
| `m_MoveType` (prediction datamap) | `0x164` |
| `m_iHealth` | `0x80` |
| `m_iMaxHealth` | not networked |
| `m_iTeamNum` | `0x88` |
| `m_vecOrigin` | `0x324` |
| `m_vecViewOffset[0]` | `0xD4` |
| `m_vecMins` / `m_vecMaxs` | `0x1A8` / `0x1B4` |
| `m_lifeState` | `0x7F` |
| `m_nModelIndex` | `0x7C` |

- Eye position = `m_vecOrigin + m_vecViewOffset` on **all three axes**.
  (The old code added the X component to Z, which left the eye ~64 units low.)
- NPC health is not networked. Alive state comes from the networked client
  `m_lifeState`; real HP is read from the listen-server `CBaseEntity` found via
  `PEntityOfEntIndex -> edict->m_pUnk -> GetBaseEntity`, using offsets scanned
  from `server.so`'s datamap:
  - `m_iHealth` `0xF4`, `m_iMaxHealth` `0xF0`,
    `m_lifeState` `0xF8`, `m_nModelIndex` `0x7A` (short).

## Menu

Starts fully collapsed on first open. `Insert` toggles; `Up`/`Down` navigate;
`Enter` expands/toggles; `Left`/`Right` edit values; `Escape` closes.
Bind rows enter bind-select mode: press a key/button to assign, `Escape` sets
None (always-on), `Enter` cancels.

```
HL2hake
  Bunnyhop
    Active
    Uni-directional auto-strafe
  ABH
    Active
  Jumpbug
    Active
  ESP
    Active
    Show neutral / healthbar / healthnum / name / skeleton / box / speed graph
  Aimbot
    Active
    Bind
    Vischeck
    Bones -> Head / Torso / Left arm / Right arm / Left leg / Right leg
    Smoothness (1..45 deg/tick, Left/Right)
    FOV (1..180 deg, Left/Right)
    Show FOV circle
  Triggerbot
    Active
    Bind
  Silent aim
    Active
    Bind
    FOV (1..180 deg, Left/Right)
```

Binds use SDL scancodes; mouse buttons are pseudo-scancodes 600..604.

## Bones / ESP

- `SetupBones(BONE_USED_BY_ANYTHING)` with engine `Time()`; studio header via
  `GetStudiomodel`; hitboxes and bone names/parents from that header.
- HL2 strips many ValveBiped bone names (the zombie's spine/legs/left arm are
  unnamed), so the humanoid skeleton is built from the **union of hitbox bone
  parent paths** (`HITGROUP_*`). Head group 1 includes the zombie headcrab body
  and its `HC_Head` child; fingers, toes and other `HC_*` limbs are excluded.
- Non-humanoid NPCs (headcrabs, crows, antlions, ...) draw the full bone tree.
- `aim_points` = head hitbox/bone + hitbox centers for the selected groups;
  the aim features pick the nearest visible point.

## Aim features

- Shared target selection (`select_aim_target`): live hostile NPCs, skip
  friendly/neutral, rank by angle to the crosshair, then pick the nearest
  selected hitgroup point. Target candidates are tried nearest-first.
- Vischeck: `EngineTraceClient003::TraceRay`, `MASK_VISIBLE`,
  `TRACE_WORLD_ONLY` (world only; static props are not tested — deliberate).
- Aimbot: bounded smoothing — 0.05 deg deadzone, max `Smoothness` deg/tick,
  shortest-path yaw, pitch clamped to ±89; pushes the new view with
  `SetViewAngles`.
- Triggerbot: crosshair ray with `MASK_SHOT` + `TRACE_EVERYTHING` (filter
  ignores the local player). First hit must be a live hostile NPC; sets
  `IN_ATTACK`.
- Silent aim: exact target angle written to `cmd->viewangles`, `IN_ATTACK`,
  then the rendered view is restored immediately **and** at
  `FRAME_RENDER_START`; `forwardmove`/`sidemove` are rewritten to preserve the
  world-space movement wish vector.
- ABH: on the landing/jump tick, face opposite the horizontal velocity with
  zero forward/side movement; HL2's jump speed-bound subtraction then
  accelerates along the velocity. Camera restored at `FRAME_RENDER_START`.

## Jumpbug

- Automatic while falling when the predicted landing speed
  `sqrt(vz^2 + 2*600*x)` exceeds `PLAYER_MAX_SAFE_FALL_SPEED` (526.5).
- The engine only applies fall damage when `CheckFalling` sees a ground entity
  at the end of the frame. `PlayerMove` runs `Duck()` **before**
  `FullWalkMove`'s `CheckJumpButton`, so a same-frame `FinishUnDuck` sets ground
  via `CategorizePosition`, the jump executes, and the fall-damage test is
  skipped.
- In air, `FinishDuck` raises the origin by 36 (standing hull 72, duck hull 36)
  and `CanUnduck` only succeeds when the floor is at least 36 below; after
  unducking, `CategorizePosition` needs it within 2 more units. The unduck
  window is therefore a 2-unit band `[36,38]` above the floor.
- The feature presses duck when the standing distance `x` is within
  `drop + [0,2]` (where `drop` is the previous command's measured fall), then
  releases duck + `IN_JUMP` on the next command when `x` is in `[36,38]`.
  It also clears `IN_JUMP` while waiting so the pogo latch is clear.
- `drop` is measured from consecutive predicted origins (not a hard-coded tick
  interval). At high fall speeds the 2-unit band can be skipped between
  commands; a higher frame rate improves reliability. Best-effort by design.

## Gotchas / lessons

- Never pipe-mask build failures:
  `./build.sh > /tmp/build.log 2>&1; echo rc=$?`.
- `CHLClient` slot numbers: `CreateMove` 21, `FrameStageNotify` 35.
- `ISurface` binary slot = SDK header slot + 4; see `src/ISURFACE_STATUS.md`.
- Silent aim and ABH temporarily replace the usercmd view; silent aim preserves
  movement, ABH intentionally zeroes it for the jump tick.
- Aimbot vischeck (`MASK_VISIBLE`/world-only) differs from triggerbot
  (`MASK_SHOT`/all entities): glass and static props can behave differently.

## Roadmap / TODO

- **Melee killbot**: 360° silent aim for hostile NPCs inside crowbar/melee
  range, sorted by distance, only while the crowbar is held.
- **GUI**: replace/augment the VGUI ISurface menu with an immediate-mode GUI
  (Dear ImGui or a custom implementation); needs a design grill first.
- **Distance weapon spread thesis**: TBD — user will explain. Likely models
  weapon spread as a function of distance for aim/trigger decisions or visuals.