diff options
| -rw-r--r-- | CONTEXT.md | 185 | ||||
| -rw-r--r-- | src/ISURFACE_STATUS.md | 36 | ||||
| -rw-r--r-- | src/hl2h.h | 5 | ||||
| -rw-r--r-- | src/menu.cpp | 24 | ||||
| -rw-r--r-- | src/netvars.cpp | 44 | ||||
| -rw-r--r-- | tree | 9 |
6 files changed, 180 insertions, 123 deletions
| @@ -1,47 +1,168 @@ | |||
| 1 | # Context: HL2 Linux Internal Cheat (`hl2hake`) | 1 | # Context: HL2 Linux Internal Cheat (`hl2hake`) |
| 2 | 2 | ||
| 3 | ## What is this project? | 3 | ## What this is |
| 4 | 4 | ||
| 5 | A minimal, featureless cheat for Half-Life 2 (native Linux build, 32-bit `hl2_linux`). Originally ported from a Windows reference (`hake`) that implemented auto-bhop, auto-ABH, and auto-climb via a `CHLClient::CreateMove` vtable hook. | 5 | A 32-bit Linux internal for Half-Life 2 (native `hl2_linux`), loaded with |
| 6 | `LD_PRELOAD` through Steam's bundled 32-bit runtime. It started as a port of | ||
| 7 | the Windows `hake` reference (auto-bhop / auto-ABH / auto-climb) and now also | ||
| 8 | covers ESP, aimbot, triggerbot and silent aim. | ||
| 6 | 9 | ||
| 7 | ## Resolved terms | 10 | ## Build / run |
| 8 | 11 | ||
| 9 | - **Build**: LD_PRELOAD a 32-bit `.so` into `hl2_linux` via Steam's bundled 32-bit runtime (`steam-runtime/run.sh`). | 12 | - `./build.sh` -> `libhl2hake.so` |
| 10 | - **Hook**: Copy-and-swap vtable on the `CHLClient` instance (slot 21 = `CreateMove`). 256-slot copy to cover all dispatched virtuals. | 13 | (`g++ -m32 -O2 -std=c++17 -Wall -Wextra -Werror -fPIC -shared`, |
| 11 | - **Netvars**: RecvTable walk from `IBaseClientDLL::GetAllClasses` (vtable slot 8). Prediction datamap scan for `CBaseEntity::m_MoveType` (networked `movetype` is a virtual proxy with offset 0). | 14 | sources: `hl2h modules cinput netvars menu esp server bones vischeck`, `-ldl`). |
| 12 | - **CInput**: Discovered via byte-scan of `CHLClient::CreateMove` prologue (gcc PIC `call thunk; add $imm,%esi` idiom), resolving the module-level `IInput*` variable. | 15 | - `./play.sh` -> Steam runtime + `hl2.sh -steam -novid -console +map d1_town_01`. |
| 13 | - **UserCMD CRC**: Standard CRC-32 (IEEE reflected, poly `0xEDB88320`, init `0xFFFFFFFF`). Field set and offsets verified against the binary's `CInput::ValidateUserCmd` disassembly. | 16 | - Logs: `/tmp/hl2hake.log` (`logf`) and stderr. `./play.sh > /tmp/hl2run.log 2>&1` |
| 14 | - **Module discovery**: `/proc/self/maps` scan for `client.so` / `engine.so` basenames (exact match at `/` boundary). PROT_NONE (`---p`) entries excluded. Merged file-backed ranges absorb one directly-adjacent anonymous `.bss` tail; anon-to-anon chaining is prohibited. | 17 | captures everything. |
| 15 | - **Calling convention**: All game virtuals use **cdecl** with `this` as first stack arg (`0x8(%ebp)` in prologues, bare `ret`). No MSVC `__thiscall`. | 18 | - Debug env: |
| 19 | - `HL2H_DRAW_TEST=1` — red probe rect + text, no menu needed. | ||
| 20 | - `HL2H_BONE_DEBUG=1` — white marker at each NPC head hitbox. | ||
| 21 | - `HL2H_NO_SERVER=1` — disable server-side health reads. | ||
| 22 | - Config: `$XDG_CONFIG_HOME/hl2hake/config`, falling back to | ||
| 23 | `~/.config/hl2hake/config`. Plain INI, saved on every change. | ||
| 16 | 24 | ||
| 17 | ## Netvar offsets (this build, verified via both RecvTable walk and pred-TD scan) | 25 | ## Runtime architecture |
| 18 | 26 | ||
| 19 | | Field | Offset | Source | | 27 | - Modules from `/proc/self/maps`: `client.so`, `engine.so`, `server.so`. |
| 20 | |---|---|---| | 28 | PROT_NONE ranges excluded; file-backed bss tails merged. |
| 21 | | `m_fFlags` | `0x33C` | RecvTable + pred TD | | 29 | - Calling convention: cdecl (i386); `this` is the first stack argument. |
| 22 | | `m_vecVelocity[0]` | `0xE0` | RecvTable (array, FIELD_VECTOR) | | 30 | - Hooks (copy-and-swap vtable, 256 slots): |
| 23 | | `m_MoveType` | `0x164` | pred-TD only (networked `movetype` has offset 0) | | 31 | - `CHLClient` slot 21 `CreateMove`; slot 35 `FrameStageNotify`. |
| 32 | - `ISurface` slot 114 `PaintTraverseEx` (primary draw); | ||
| 33 | slot 22 `DrawPrintText` fallback. Draws wrapped in | ||
| 34 | `PushMakeCurrent`/`PopMakeCurrent`. | ||
| 35 | - `CInput*` discovered by byte-scanning the `CHLClient::CreateMove` prologue. | ||
| 36 | Usercmd CRC-32 is repaired after any cmd edit (`note_modified`). | ||
| 37 | - Console output via `ConMsg` from `libtier0.so` | ||
| 38 | (`_Z6ConMsgPKcz`), not `IVEngineClient::ClientCmd`. | ||
| 24 | 39 | ||
| 25 | ## Key engine index table (IVEngineClient014, this build) | 40 | ## Interfaces / indices |
| 26 | 41 | ||
| 27 | | Index | Method | Verification | | 42 | | Interface | Version | Notes | |
| 28 | |---|---|---| | 43 | |---|---|---| |
| 29 | | 8 | *(not ClientCmd)* | Disasm: per-player index bounds check on `(arg-1)` — likely `GetPlayerInfo` | | 44 | | Client | `VClient017` | | |
| 30 | | 9 | *(not ClientCmd_Unrestricted)* | Disasm: loop over split-screen players — no-op'd in single-player | | 45 | | Entity list | `VClientEntityList003` | | |
| 31 | | 12 | `GetLocalPlayer` | Disasm: `return *(CClientState + 0x1b0) + 1` — confirmed in-game | | 46 | | Engine | `VEngineClient014` | | |
| 32 | | 65 | `HudUpdate`-family | Crash-site (256-slot bump): `call *(this, float)` | | 47 | | Model info | `VModelInfoClient006` | | |
| 48 | | Engine server | `VEngineServer021` | edicts / listen-server entities | | ||
| 49 | | Debug overlay | `VDebugOverlay003` | accurate world->screen | | ||
| 50 | | Engine trace | `EngineTraceClient003` | vischeck / trigger ray | | ||
| 51 | | Surface | `VGUI_Surface030` | | | ||
| 52 | |||
| 53 | Relevant vtable slots (this build): | ||
| 54 | |||
| 55 | - Engine: `GetLocalPlayer` 12, `Time` 14, `GetViewAngles` 19, `SetViewAngles` 20. | ||
| 56 | - Entity list: `GetClientNetworkable` 0, `GetClientEntity` 3, | ||
| 57 | `GetHighestEntityIndex` 6. | ||
| 58 | - `IClientNetworkable`: `GetClientClass` 2. `ClientClass::m_pNetworkName` at +8. | ||
| 59 | - `IClientUnknown`: `GetClientRenderable` 6. | ||
| 60 | - `IClientRenderable`: `GetModel` 9, `SetupBones` 16. | ||
| 61 | - `IVModelInfo`: `GetModel` 2, `GetModelName` 4, `GetStudiomodel` 29. | ||
| 62 | - `IVEngineServer`: `PEntityOfEntIndex` 19. | ||
| 63 | Edict `m_pUnk` at +0xC; `IServerUnknown::GetBaseEntity` 6. | ||
| 64 | - `IVDebugOverlay`: `ScreenPosition` 9. | ||
| 65 | - `IEngineTrace`: `TraceRay` 4. `Ray_t` uses 16-byte `VectorAligned`; | ||
| 66 | `trace_t`: `fraction` 44, `startsolid` 55, `m_pEnt` 76. | ||
| 67 | |||
| 68 | ## Netvars / entity data (this build) | ||
| 69 | |||
| 70 | | Field | Client offset | | ||
| 71 | |---|---| | ||
| 72 | | `m_fFlags` | `0x33C` | | ||
| 73 | | `m_vecVelocity[0]` | `0xE0` | | ||
| 74 | | `m_MoveType` (prediction datamap) | `0x164` | | ||
| 75 | | `m_iHealth` | `0x80` | | ||
| 76 | | `m_iMaxHealth` | not networked | | ||
| 77 | | `m_iTeamNum` | `0x88` | | ||
| 78 | | `m_vecOrigin` | `0x324` | | ||
| 79 | | `m_vecViewOffset[0]` | `0xD4` | | ||
| 80 | | `m_vecMins` / `m_vecMaxs` | `0x1A8` / `0x1B4` | | ||
| 81 | | `m_lifeState` | `0x7F` | | ||
| 82 | | `m_nModelIndex` | `0x7C` | | ||
| 83 | |||
| 84 | - Eye position = `m_vecOrigin + m_vecViewOffset` on **all three axes**. | ||
| 85 | (The old code added the X component to Z, which left the eye ~64 units low.) | ||
| 86 | - NPC health is not networked. Alive state comes from the networked client | ||
| 87 | `m_lifeState`; real HP is read from the listen-server `CBaseEntity` found via | ||
| 88 | `PEntityOfEntIndex -> edict->m_pUnk -> GetBaseEntity`, using offsets scanned | ||
| 89 | from `server.so`'s datamap: | ||
| 90 | - `m_iHealth` `0xF4`, `m_iMaxHealth` `0xF0`, | ||
| 91 | `m_lifeState` `0xF8`, `m_nModelIndex` `0x7A` (short). | ||
| 92 | |||
| 93 | ## Menu | ||
| 94 | |||
| 95 | Starts fully collapsed on first open. `Insert` toggles; `Up`/`Down` navigate; | ||
| 96 | `Enter` expands/toggles; `Left`/`Right` edit values; `Escape` closes. | ||
| 97 | Bind rows enter bind-select mode: press a key/button to assign, `Escape` sets | ||
| 98 | None (always-on), `Enter` cancels. | ||
| 99 | |||
| 100 | ``` | ||
| 101 | HL2hake | ||
| 102 | Bunnyhop | ||
| 103 | Active | ||
| 104 | Uni-directional auto-strafe | ||
| 105 | ABH | ||
| 106 | Active | ||
| 107 | ESP | ||
| 108 | Active | ||
| 109 | Show neutral / healthbar / healthnum / name / skeleton / box | ||
| 110 | Aimbot | ||
| 111 | Active | ||
| 112 | Bind | ||
| 113 | Vischeck | ||
| 114 | Bones -> Head / Torso / Left arm / Right arm / Left leg / Right leg | ||
| 115 | Smoothness (1..45 deg/tick, Left/Right) | ||
| 116 | Triggerbot | ||
| 117 | Active | ||
| 118 | Bind | ||
| 119 | Silent aim | ||
| 120 | Active | ||
| 121 | Bind | ||
| 122 | ``` | ||
| 123 | |||
| 124 | Binds use SDL scancodes; mouse buttons are pseudo-scancodes 600..604. | ||
| 125 | |||
| 126 | ## Bones / ESP | ||
| 33 | 127 | ||
| 34 | Console output uses `ConMsg` from `libtier0.so` (resolved via `dlsym(RTLD_DEFAULT, "_Z6ConMsgPKcz")`), not `IVEngineClient::ClientCmd`. | 128 | - `SetupBones(BONE_USED_BY_ANYTHING)` with engine `Time()`; studio header via |
| 129 | `GetStudiomodel`; hitboxes and bone names/parents from that header. | ||
| 130 | - HL2 strips many ValveBiped bone names (the zombie's spine/legs/left arm are | ||
| 131 | unnamed), so the humanoid skeleton is built from the **union of hitbox bone | ||
| 132 | parent paths** (`HITGROUP_*`). Head group 1 includes the zombie headcrab body | ||
| 133 | and its `HC_Head` child; fingers, toes and other `HC_*` limbs are excluded. | ||
| 134 | - Non-humanoid NPCs (headcrabs, crows, antlions, ...) draw the full bone tree. | ||
| 135 | - `aim_points` = head hitbox/bone + hitbox centers for the selected groups; | ||
| 136 | the aim features pick the nearest visible point. | ||
| 35 | 137 | ||
| 36 | ## Menu system | 138 | ## Aim features |
| 37 | 139 | ||
| 38 | - **Toggle architecture**: each feature is runtime-toggleable, ON by default. A `MenuState` struct holds per-feature `active` bool and `bind` (key code or None). | 140 | - Shared target selection (`select_aim_target`): live hostile NPCs, skip |
| 39 | - **Rendering**: hook `IEngineVGui::Paint` via `VEngineVGui001` — draws every frame including the main menu. Text-only overlay in top-left corner. | 141 | friendly/neutral, rank by angle to the crosshair, then pick the nearest |
| 40 | - **Persistence**: INI-style plain text config; one line per setting (`climb_active=1`, `climb_bind=MOUSE3`). Config written on every state change; loaded before hooks install. | 142 | selected hitgroup point. Target candidates are tried nearest-first. |
| 41 | - **Input model**: Insert toggles menu open/closed; arrow keys navigate the tree; Enter expands/collapses nodes and toggles boolean fields; Escape closes the menu. Modal while open (all game input blocked). Long-term: selective pass-through for unused inputs. | 143 | - Vischeck: `EngineTraceClient003::TraceRay`, `MASK_VISIBLE`, |
| 42 | - **Bind assignment UX**: selecting a Bind node enters bind-select mode; any key press becomes the new bind; Escape sets bind to None (disabling the feature); Enter cancels bind change and reverts. | 144 | `TRACE_WORLD_ONLY` (world only; static props are not tested — deliberate). |
| 43 | - **Tree layout**: HL2hake (root) → Climb (Active + Bind), Bunnyhop (Active), ABH (Active). Children indented. All nodes expanded by default when the menu opens. | 145 | - Aimbot: bounded smoothing — 0.05 deg deadzone, max `Smoothness` deg/tick, |
| 146 | shortest-path yaw, pitch clamped to ±89; pushes the new view with | ||
| 147 | `SetViewAngles`. | ||
| 148 | - Triggerbot: crosshair ray with `MASK_SHOT` + `TRACE_EVERYTHING` (filter | ||
| 149 | ignores the local player). First hit must be a live hostile NPC; sets | ||
| 150 | `IN_ATTACK`. | ||
| 151 | - Silent aim: exact target angle written to `cmd->viewangles`, `IN_ATTACK`, | ||
| 152 | then the rendered view is restored immediately **and** at | ||
| 153 | `FRAME_RENDER_START`; `forwardmove`/`sidemove` are rewritten to preserve the | ||
| 154 | world-space movement wish vector. | ||
| 155 | - ABH: on the landing/jump tick, face opposite the horizontal velocity with | ||
| 156 | zero forward/side movement; HL2's jump speed-bound subtraction then | ||
| 157 | accelerates along the velocity. Camera restored at `FRAME_RENDER_START`. | ||
| 44 | 158 | ||
| 45 | ## Open questions | 159 | ## Gotchas / lessons |
| 46 | 160 | ||
| 47 | None currently. | 161 | - Never pipe-mask build failures: |
| 162 | `./build.sh > /tmp/build.log 2>&1; echo rc=$?`. | ||
| 163 | - `CHLClient` slot numbers: `CreateMove` 21, `FrameStageNotify` 35. | ||
| 164 | - `ISurface` binary slot = SDK header slot + 4; see `src/ISURFACE_STATUS.md`. | ||
| 165 | - Silent aim and ABH temporarily replace the usercmd view; silent aim preserves | ||
| 166 | movement, ABH intentionally zeroes it for the jump tick. | ||
| 167 | - Aimbot vischeck (`MASK_VISIBLE`/world-only) differs from triggerbot | ||
| 168 | (`MASK_SHOT`/all entities): glass and static props can behave differently. | ||
diff --git a/src/ISURFACE_STATUS.md b/src/ISURFACE_STATUS.md index de952cd..00a7adf 100644 --- a/src/ISURFACE_STATUS.md +++ b/src/ISURFACE_STATUS.md | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | # HL2hake ISurface Drawing — Status Report | 1 | # HL2hake ISurface Drawing — Status Report |
| 2 | 2 | ||
| 3 | ## Date: 2026-09-12 (final round) | 3 | ## Date: 2026-09-12, updated 2026-10-26 |
| 4 | 4 | ||
| 5 | ## Result: WORKING | 5 | ## Result: WORKING |
| 6 | 6 | ||
| 7 | Overlay drawing works in-game and in menus. Draw path: `PaintTraverseEx` | 7 | Overlay drawing works in-game and in menus. Draw path: `PaintTraverseEx` |
| 8 | hook (slot 114) on the copied ISurface vtable, wrapping our draws in | 8 | hook (slot 114) on the copied ISurface vtable, wrapping our draws in |
| 9 | `PushMakeCurrent(panel, false)` / `PopMakeCurrent(panel)` around ISurface | 9 | `PushMakeCurrent(panel, false)` / `PopMakeCurrent(panel)` around ISurface |
| 10 | slots 6/10/12/17/18/20/22/66/67. | 10 | slots 6/10/12/15/17/18/20/22/66/67. |
| 11 | 11 | ||
| 12 | ## Confirmed ISurface VTable Slots (disassembly-verified, binary=SDK header+4) | 12 | ## Confirmed ISurface VTable Slots (disassembly-verified, binary=SDK header+4) |
| 13 | 13 | ||
| @@ -18,6 +18,8 @@ slots 6/10/12/17/18/20/22/66/67. | |||
| 18 | | 9 | `PopMakeCurrent(VPANEL)` | verified via disassembly | | 18 | | 9 | `PopMakeCurrent(VPANEL)` | verified via disassembly | |
| 19 | | 10 | `DrawSetColor(int r,g,b,a)` | stores RGBA at `this+0x24..0x27` | | 19 | | 10 | `DrawSetColor(int r,g,b,a)` | stores RGBA at `this+0x24..0x27` | |
| 20 | | 12 | `DrawFilledRect(int x0,y0,x1,y1)` | verified via disassembly | | 20 | | 12 | `DrawFilledRect(int x0,y0,x1,y1)` | verified via disassembly | |
| 21 | | 14 | `DrawOutlinedRect(int x0,y0,x1,y1)` | SDK header 10 + 4 | | ||
| 22 | | 15 | `DrawLine(int x0,y0,x1,y1)` | SDK header 11 + 4 (skeleton ESP) | | ||
| 21 | | 17 | `DrawSetTextFont(HFont)` | stores HFont at `this+0x290` | | 23 | | 17 | `DrawSetTextFont(HFont)` | stores HFont at `this+0x290` | |
| 22 | | 18 | `DrawSetTextColor(int r,g,b,a)` | stores RGBA at `this+0x28..0x2b` | | 24 | | 18 | `DrawSetTextColor(int r,g,b,a)` | stores RGBA at `this+0x28..0x2b` | |
| 23 | | 20 | `DrawSetTextPos(int x,y)` | stores at `this+0x2c/0x30` | | 25 | | 20 | `DrawSetTextPos(int x,y)` | stores at `this+0x2c/0x30` | |
| @@ -65,24 +67,27 @@ fc-match findings (system fontconfig): | |||
| 65 | 67 | ||
| 66 | ## Menu | 68 | ## Menu |
| 67 | 69 | ||
| 68 | - Tree: HL2hake → Bunnyhop/ABH, children indented (`g_depth[]`, +12px/level). | 70 | - Starts fully collapsed on first open; `Insert` toggles it. |
| 69 | - Right-aligned state at x=200: `[on]`/`[off]` for Active rows, | 71 | - Tree: HL2hake -> Bunnyhop (Active, Auto-strafe), ABH (Active), ESP (Active, |
| 70 | `[keyname]` for Bind rows, `[press key]` while binding. | 72 | Show neutral/healthbar/healthnum/name/skeleton/box), Aimbot (Active, Bind, |
| 73 | Vischeck, Bones, Smoothness), Triggerbot (Active, Bind), Silent aim | ||
| 74 | (Active, Bind). Children indented by `g_depth[]` (+12px/level). | ||
| 75 | - Right-aligned state at x=270: `[on]`/`[off]` for toggles, `[keyname]` for | ||
| 76 | binds, `[press key]` while binding, `N deg/tick` for values. | ||
| 71 | - Feature gating: `feature_active(int)` (menu.cpp) — active AND | 77 | - Feature gating: `feature_active(int)` (menu.cpp) — active AND |
| 72 | (bind==0 or key held). Called from `do_move` (hl2h.cpp) to gate | 78 | (bind==0 or key held), used by `do_move` and the aim features. |
| 73 | bhop (jump) and ABH branches. | 79 | - Config: `$XDG_CONFIG_HOME/hl2hake/config` (fallback `~/.config/hl2hake/config`). |
| 74 | - Config: INI at `~/.hl2hake.ini` (bhop/abh active+bind). | ||
| 75 | 80 | ||
| 76 | ## Build / Run | 81 | ## Build / Run |
| 77 | 82 | ||
| 78 | - `./build.sh`: g++ `-m32 -O2 -fPIC -shared`, links 5 TUs, `-ldl`, | 83 | - `./build.sh`: g++ `-m32 -O2 -std=c++17 -fPIC -shared`, 9 TUs |
| 84 | (`hl2h modules cinput netvars menu esp server bones vischeck`), `-ldl`, | ||
| 79 | `-Wall -Wextra -Werror` (all warnings fatal). | 85 | `-Wall -Wextra -Werror` (all warnings fatal). |
| 80 | - `./play.sh -windowed +map d1_trainstation_01` (Steam runtime wrapper, | 86 | - `./play.sh -windowed +map d1_trainstation_01` (Steam runtime wrapper, |
| 81 | `LD_PRELOAD=libhl2hake.so`). | 87 | `LD_PRELOAD=libhl2hake.so`). |
| 82 | - Logs: `/tmp/hl2hake.log` (logf), `/tmp/hl2run.log` (stderr). | 88 | - Logs: `/tmp/hl2hake.log` (logf), stderr. |
| 83 | - Probe logging (`hook fired:`, `probe:`) removed 2026-09-12. | ||
| 84 | - `HL2H_DRAW_TEST=1` env: draws red rect + "HL2H DRAW TEST" without | 89 | - `HL2H_DRAW_TEST=1` env: draws red rect + "HL2H DRAW TEST" without |
| 85 | opening the menu. | 90 | opening the menu. `HL2H_BONE_DEBUG=1` draws head-hitbox markers. |
| 86 | 91 | ||
| 87 | ## Lessons | 92 | ## Lessons |
| 88 | 93 | ||
| @@ -98,7 +103,12 @@ fc-match findings (system fontconfig): | |||
| 98 | ## File Locations | 103 | ## File Locations |
| 99 | 104 | ||
| 100 | - Project: `/home/salmon/Documents/rust/hl2h/` | 105 | - Project: `/home/salmon/Documents/rust/hl2h/` |
| 101 | - Hook + feature logic: `src/hl2h.cpp` | 106 | - Hook + movement/feature logic: `src/hl2h.cpp` |
| 102 | - Menu + drawing + config + input: `src/menu.cpp` | 107 | - Menu + drawing + config + input: `src/menu.cpp` |
| 108 | - ESP / skeleton drawing / aimbot / silent aim: `src/esp.cpp` | ||
| 109 | - Bones + hitboxes: `src/bones.cpp` | ||
| 110 | - Server-side entity/health access: `src/server.cpp` | ||
| 111 | - World vischeck / entity trace: `src/vischeck.cpp` | ||
| 103 | - Shared API: `src/hl2h.h` | 112 | - Shared API: `src/hl2h.h` |
| 113 | - Current project overview: `CONTEXT.md` | ||
| 104 | - SDK headers: `source-sdk-2013/src/public/vgui/` | 114 | - SDK headers: `source-sdk-2013/src/public/vgui/` |
| @@ -168,7 +168,6 @@ void fix_verified_crc(int seq, uint32_t crc); | |||
| 168 | uint32_t usercmd_checksum(const CUserCmd *c); | 168 | uint32_t usercmd_checksum(const CUserCmd *c); |
| 169 | bool find_input_global(); | 169 | bool find_input_global(); |
| 170 | void resolve_netvars(); | 170 | void resolve_netvars(); |
| 171 | int class_health_off(const char *name); | ||
| 172 | bool resolve_server_netvars(); | 171 | bool resolve_server_netvars(); |
| 173 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); | 172 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); |
| 174 | int server_model_index(void *base); | 173 | int server_model_index(void *base); |
| @@ -203,10 +202,6 @@ bool head_position(int idx, Vector &out); | |||
| 203 | int aim_points(int idx, unsigned group_mask, Vector *out, int max); | 202 | int aim_points(int idx, unsigned group_mask, Vector *out, int max); |
| 204 | 203 | ||
| 205 | void outer_draw(); | 204 | void outer_draw(); |
| 206 | // raw ISurface slot calls used by the PaintTraverseEx hook (hl2h.cpp) | ||
| 207 | using SurfFn = void (*)(void *, ...); | ||
| 208 | long surf_call_slot(void *surf, int slot, long a1 = 0, long a2 = 0); | ||
| 209 | |||
| 210 | // ISurface pointer (defined in menu.cpp, used for drawing tests) | 205 | // ISurface pointer (defined in menu.cpp, used for drawing tests) |
| 211 | struct ISurface; | 206 | struct ISurface; |
| 212 | extern ISurface *g_surface; | 207 | extern ISurface *g_surface; |
diff --git a/src/menu.cpp b/src/menu.cpp index c3bb9ae..d628873 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -217,22 +217,6 @@ static std::string config_path() { | |||
| 217 | return dir + "/config"; | 217 | return dir + "/config"; |
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | [[maybe_unused]] static const char *sdl_scancode_name(int sc) { | ||
| 221 | if (sc == 0) | ||
| 222 | return "None"; | ||
| 223 | static char buf[32]; | ||
| 224 | snprintf(buf, sizeof(buf), "Key%d", sc); | ||
| 225 | return buf; | ||
| 226 | } | ||
| 227 | |||
| 228 | [[maybe_unused]] static int parse_sdl_scancode(const char *s) { | ||
| 229 | if (strcmp(s, "None") == 0 || strcmp(s, "") == 0) | ||
| 230 | return 0; | ||
| 231 | if (strncmp(s, "Key", 3) == 0) | ||
| 232 | return atoi(s + 3); | ||
| 233 | return 0; | ||
| 234 | } | ||
| 235 | |||
| 236 | static void config_save() { | 220 | static void config_save() { |
| 237 | FILE *f = fopen(config_path().c_str(), "w"); | 221 | FILE *f = fopen(config_path().c_str(), "w"); |
| 238 | if (!f) | 222 | if (!f) |
| @@ -758,14 +742,6 @@ void outer_draw() { | |||
| 758 | surf_pop_make_current(panel); | 742 | surf_pop_make_current(panel); |
| 759 | } | 743 | } |
| 760 | 744 | ||
| 761 | // Call an arbitrary ISurface vtable slot with up to 2 long args (cdecl thiscall | ||
| 762 | // on i386: this first, rest on stack). | ||
| 763 | long surf_call_slot(void *surf, int slot, long a1, long a2) { | ||
| 764 | void **vt = *(void ***)surf; | ||
| 765 | using Fn = long (*)(void *, long, long); | ||
| 766 | return ((Fn)vt[slot])(surf, a1, a2); | ||
| 767 | } | ||
| 768 | |||
| 769 | // --------------------------------------------------------------------------- | 745 | // --------------------------------------------------------------------------- |
| 770 | // ISurface discovery — try known modules and all loaded .so files | 746 | // ISurface discovery — try known modules and all loaded .so files |
| 771 | // --------------------------------------------------------------------------- | 747 | // --------------------------------------------------------------------------- |
diff --git a/src/netvars.cpp b/src/netvars.cpp index f702c41..b4e31d5 100644 --- a/src/netvars.cpp +++ b/src/netvars.cpp | |||
| @@ -27,24 +27,7 @@ static bool module_string(uintptr_t p, char *out, size_t outn) { | |||
| 27 | 27 | ||
| 28 | static int g_dumped_tables = 0; | 28 | static int g_dumped_tables = 0; |
| 29 | 29 | ||
| 30 | // per-client-class health offset (m_iHealth lives at different offsets per | 30 | static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth) { |
| 31 | // class chain: CBasePlayer via DT_BasePlayer=128, NPCs via DT_BaseCombatCharacter) | ||
| 32 | static struct { | ||
| 33 | char name[32]; | ||
| 34 | int off; | ||
| 35 | } g_class_health[256]; | ||
| 36 | static int g_class_health_n = 0; | ||
| 37 | |||
| 38 | int class_health_off(const char *name) { | ||
| 39 | for (int i = 0; i < g_class_health_n; i++) | ||
| 40 | if (strcmp(name, g_class_health[i].name) == 0) | ||
| 41 | return g_class_health[i].off; | ||
| 42 | return -1; | ||
| 43 | } | ||
| 44 | |||
| 45 | static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth, | ||
| 46 | int *out_health) | ||
| 47 | { | ||
| 48 | if (depth > 8) | 31 | if (depth > 8) |
| 49 | return; | 32 | return; |
| 50 | uintptr_t props = *(uintptr_t *)(table + 0x00); | 33 | uintptr_t props = *(uintptr_t *)(table + 0x00); |
| @@ -85,13 +68,10 @@ static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth, | |||
| 85 | logf(" [%s] %s type=%d off=%d(0x%x) table=%s array=%s", tname, pname2, | 68 | logf(" [%s] %s type=%d off=%d(0x%x) table=%s array=%s", tname, pname2, |
| 86 | ptype, poff, poff, ptable ? "yes" : "no", parray ? "yes" : "no"); | 69 | ptype, poff, poff, ptable ? "yes" : "no", parray ? "yes" : "no"); |
| 87 | if (ptable) { | 70 | if (ptable) { |
| 88 | collect_netvars(ptable, base_off + (uint32_t)poff, depth + 1, | 71 | collect_netvars(ptable, base_off + (uint32_t)poff, depth + 1); |
| 89 | out_health); | ||
| 90 | continue; | 72 | continue; |
| 91 | } | 73 | } |
| 92 | 74 | ||
| 93 | if (out_health && *out_health < 0 && strcmp(pname2, "m_iHealth") == 0) | ||
| 94 | *out_health = (int)(base_off + (uint32_t)poff); | ||
| 95 | if (strcmp(pname2, "m_lifeState") == 0 && g_net.lifestate < 0) { | 75 | if (strcmp(pname2, "m_lifeState") == 0 && g_net.lifestate < 0) { |
| 96 | g_net.lifestate = (int)(base_off + (uint32_t)poff); | 76 | g_net.lifestate = (int)(base_off + (uint32_t)poff); |
| 97 | } else if (strcmp(pname2, "m_nModelIndex") == 0 && g_net.modelindex < 0) { | 77 | } else if (strcmp(pname2, "m_nModelIndex") == 0 && g_net.modelindex < 0) { |
| @@ -217,25 +197,9 @@ void resolve_netvars() { | |||
| 217 | for (uintptr_t cc = head; cc && g_client.contains(cc) && classes < 1024; | 197 | for (uintptr_t cc = head; cc && g_client.contains(cc) && classes < 1024; |
| 218 | cc = *(uintptr_t *)(cc + 0x10)) { | 198 | cc = *(uintptr_t *)(cc + 0x10)) { |
| 219 | classes++; | 199 | classes++; |
| 220 | char cname[32]; | 200 | collect_netvars(*(uintptr_t *)(cc + 0x0C), 0, 0); |
| 221 | int hoff = -1; | ||
| 222 | // per-class health offset — NPCs send m_iHealth via their own table chain, | ||
| 223 | // not DT_BasePlayer's; g_net.health keeps the player's for aimbot use | ||
| 224 | if (module_string(*(uintptr_t *)(cc + 0x08), cname, sizeof(cname))) { | ||
| 225 | collect_netvars(*(uintptr_t *)(cc + 0x0C), 0, 0, &hoff); | ||
| 226 | if (hoff >= 0 && g_class_health_n < 256) { | ||
| 227 | char *dst = g_class_health[g_class_health_n].name; | ||
| 228 | const char *src = cname; | ||
| 229 | size_t n = sizeof(g_class_health[g_class_health_n].name) - 1; | ||
| 230 | for (size_t i = 0; i < n && src[i]; i++) dst[i] = src[i]; | ||
| 231 | dst[n] = 0; | ||
| 232 | g_class_health[g_class_health_n].off = hoff; | ||
| 233 | g_class_health_n++; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | 201 | } |
| 237 | logf("client classes walked: %d, health-offset classes: %d", classes, | 202 | logf("client classes walked: %d", classes); |
| 238 | g_class_health_n); | ||
| 239 | 203 | ||
| 240 | if (g_net.movetype < 0 && !resolve_movetype()) { | 204 | if (g_net.movetype < 0 && !resolve_movetype()) { |
| 241 | // movetype unresolved — bhop still works, just no noclip/ladder guard | 205 | // movetype unresolved — bhop still works, just no noclip/ladder guard |
| @@ -1,9 +0,0 @@ | |||
| 1 | HL2hake | ||
| 2 | |||
| 3 | Climb | ||
| 4 | Active: <state> | ||
| 5 | Bind: <bind> | ||
| 6 | Bunnyhop | ||
| 7 | Active: <state> | ||
| 8 | ABH | ||
| 9 | Active: <state> | ||
