diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-06 16:09:55 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-06 16:09:55 +0100 |
| commit | 33f5ed9ef58f26c6b3b52cf605dd4311d658a60d (patch) | |
| tree | a97254872e465c9107b7ef19695ab56bb076d7e2 /src/server.cpp | |
| parent | Items: log the usable ammo table once (diff) | |
| download | hl2h-33f5ed9ef58f26c6b3b52cf605dd4311d658a60d.tar.gz hl2h-33f5ed9ef58f26c6b3b52cf605dd4311d658a60d.zip | |
Fix audit findings: crash safety, correctness, rendering, performance
Crash/UB:
- gate every hook behind an init-stage flag until config/surface/input are ready
- publish module maps as immutable atomic snapshots instead of reallocating
shared vectors under unlocked readers
- validate the %ebp walk in the RandomSeed/RandomFloat hooks against the stack
- bounds-check every RecvTable/RecvProp/string dereference with module_range()
- preserve the Itanium vtable header when cloning (client, CInput, model render,
studio render)
- reject negative usercmd sequence numbers; guard config_load against a null
section target; cap the bone parent walk; check mprotect; drop PROT_EXEC on
the GOT patch; absorb server.so .bss tail
Correctness:
- filter dormant entities across ESP, aimbot, triggerbot, melee, items and
projectiles (slot self-tested against the local player)
- reset the server-health latch per map and only latch on valid model indices
- bone cache sized for the 128-target scan; set valid=false on every failure;
head_position keeps searching past an invalid bone
- vischeck steps past glass by probing until clear, and ignores entities owned
by the target (held weapons, bone followers)
- aimbot: maxs no longer disables it, AABB-aware FOV prefilter, lock hysteresis,
lead point re-checked for FOV and visibility
- spread: seed hook arms on the no-spread toggle alone; engine toggle only
controls the RandomFloat zeroing
- resolve m_hOwnerEntity/m_hOwner; NPC lead velocity from the server entity
- vector_angles pitch range, point_ground normal test, underwater jump+duck no
longer skips combat, semi-auto press/release edge for synthetic fire
- item/projectile markers and blast rings refresh origin per frame while
discovery stays at 10 Hz; trajectory uses the real m_DmgRadius and honours
the blast-radius toggle
Rendering/GUI:
- one overlay draw per engine frame (FRAME_RENDER_START boundary), fixing
alpha stacking and the speed-graph flicker
- clipped lines/outlines and ellipsised text instead of drawing past the
content pane or shifting over neighbours
- dropdown dismissal consumes the click; hover ignores hidden controls
- chams: reset AlphaModulate, reference shared materials once, restore prop
tint colour; fonts created once per handle
- projection uses Source's 4:3 aspect scaling and roll; screen size refreshed
each frame; FOV circle gated on the aim toggles, not the bind
Performance:
- TTL caches for NPC relation, friendly/neutral and damage class
- model names memoised per model index
- entity pointer -> index cache for the triggerbot (was a 4096-slot walk/tick)
Build:
- real Makefile with -MMD dependency tracking, debug/release objects and an
always-relink rule; build.sh kept as a thin wrapper
Diffstat (limited to '')
| -rw-r--r-- | src/server.cpp | 27 |
1 files changed, 20 insertions, 7 deletions
diff --git a/src/server.cpp b/src/server.cpp index b1661ac..0d14c59 100644 --- a/src/server.cpp +++ b/src/server.cpp | |||
| @@ -68,7 +68,7 @@ static bool g_srv_checked = false; | |||
| 68 | 68 | ||
| 69 | static uintptr_t find_string(const char *s) { | 69 | static uintptr_t find_string(const char *s) { |
| 70 | size_t n = strlen(s) + 1; | 70 | size_t n = strlen(s) + 1; |
| 71 | for (auto &r : g_server.ranges) { | 71 | for (auto &r : mod_server().ranges) { |
| 72 | const uint8_t *base = (const uint8_t *)r.lo; | 72 | const uint8_t *base = (const uint8_t *)r.lo; |
| 73 | size_t span = r.hi - r.lo; | 73 | size_t span = r.hi - r.lo; |
| 74 | for (size_t i = 0; i + n <= span; i++) | 74 | for (size_t i = 0; i + n <= span; i++) |
| @@ -92,7 +92,7 @@ static int datamap_offsets(const char *name, int want_type, int min_off, | |||
| 92 | if (!str) | 92 | if (!str) |
| 93 | return 0; | 93 | return 0; |
| 94 | int n = 0; | 94 | int n = 0; |
| 95 | for (auto &r : g_server.ranges) { | 95 | for (auto &r : mod_server().ranges) { |
| 96 | const uint8_t *base = (const uint8_t *)r.lo; | 96 | const uint8_t *base = (const uint8_t *)r.lo; |
| 97 | size_t span = r.hi - r.lo; | 97 | size_t span = r.hi - r.lo; |
| 98 | for (size_t off = 4; off + 0x18 <= span; off += 4) { | 98 | for (size_t off = 4; off + 0x18 <= span; off += 4) { |
| @@ -111,7 +111,7 @@ static int datamap_offsets(const char *name, int want_type, int min_off, | |||
| 111 | continue; | 111 | continue; |
| 112 | // DEFINE_KEYFIELD entries have an external name ("health"); plain | 112 | // DEFINE_KEYFIELD entries have an external name ("health"); plain |
| 113 | // DEFINE_FIELD entries have none. Either way it must be in server.so. | 113 | // DEFINE_FIELD entries have none. Either way it must be in server.so. |
| 114 | if (ext && !g_server.contains(ext)) | 114 | if (ext && !mod_server().contains(ext)) |
| 115 | continue; | 115 | continue; |
| 116 | bool dup = false; | 116 | bool dup = false; |
| 117 | for (int i = 0; i < n; i++) | 117 | for (int i = 0; i < n; i++) |
| @@ -144,9 +144,9 @@ bool resolve_server_netvars() { | |||
| 144 | if ((retry++ % 100) != 0) | 144 | if ((retry++ % 100) != 0) |
| 145 | return false; | 145 | return false; |
| 146 | // server.so is only mapped once a map is running; refresh the module list. | 146 | // server.so is only mapped once a map is running; refresh the module list. |
| 147 | if (!g_server.valid()) | 147 | if (!mod_server().valid()) |
| 148 | find_modules(); | 148 | find_modules(); |
| 149 | if (!g_server.valid()) { | 149 | if (!mod_server().valid()) { |
| 150 | if (!fail_logged) { | 150 | if (!fail_logged) { |
| 151 | fail_logged = true; | 151 | fail_logged = true; |
| 152 | logd("server datamap: server.so not mapped yet"); | 152 | logd("server datamap: server.so not mapped yet"); |
| @@ -209,7 +209,7 @@ bool resolve_server_netvars() { | |||
| 209 | "followtarget=%d spawnflags=%d hacked=%d npcstate=%d " | 209 | "followtarget=%d spawnflags=%d hacked=%d npcstate=%d " |
| 210 | "takedamage=%d mapname=%d armor=%d detonate=%d dmgradius=%d " | 210 | "takedamage=%d mapname=%d armor=%d detonate=%d dmgradius=%d " |
| 211 | "ammo=%d myweapons=%d juice_suit=%d juice_health=%d", | 211 | "ammo=%d myweapons=%d juice_suit=%d juice_health=%d", |
| 212 | g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, | 212 | mod_server().path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, |
| 213 | g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, | 213 | g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, |
| 214 | g_srv_activeweapon, g_srv_relationship, g_srv_followtarget, | 214 | g_srv_activeweapon, g_srv_relationship, g_srv_followtarget, |
| 215 | g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage, | 215 | g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage, |
| @@ -219,7 +219,7 @@ bool resolve_server_netvars() { | |||
| 219 | } else if (!fail_logged) { | 219 | } else if (!fail_logged) { |
| 220 | fail_logged = true; | 220 | fail_logged = true; |
| 221 | logf("server datamap: scan failed (server.so at %s)", | 221 | logf("server datamap: scan failed (server.so at %s)", |
| 222 | g_server.path.c_str()); | 222 | mod_server().path.c_str()); |
| 223 | toastf(TOAST_WARN, "Server datamap scan failed; jumpbug assist limited"); | 223 | toastf(TOAST_WARN, "Server datamap scan failed; jumpbug assist limited"); |
| 224 | } | 224 | } |
| 225 | return g_srv_ready; | 225 | return g_srv_ready; |
| @@ -275,6 +275,19 @@ int server_entity_health(int idx, int *maxhealth, int *lifestate, | |||
| 275 | return hp; | 275 | return hp; |
| 276 | } | 276 | } |
| 277 | 277 | ||
| 278 | bool server_entity_velocity(int idx, Vector *out) { | ||
| 279 | if (!out || g_srv_velocity <= 0) | ||
| 280 | return false; | ||
| 281 | void *base = nullptr; | ||
| 282 | if (server_entity_health(idx, nullptr, nullptr, &base) < 0 || !base) | ||
| 283 | return false; | ||
| 284 | uintptr_t b = (uintptr_t)base; | ||
| 285 | out->x = *(float *)(b + g_srv_velocity); | ||
| 286 | out->y = *(float *)(b + g_srv_velocity + 4); | ||
| 287 | out->z = *(float *)(b + g_srv_velocity + 8); | ||
| 288 | return true; | ||
| 289 | } | ||
| 290 | |||
| 278 | int server_model_index(void *base) { | 291 | int server_model_index(void *base) { |
| 279 | if (!base || g_srv_modelindex <= 0) | 292 | if (!base || g_srv_modelindex <= 0) |
| 280 | return -1; | 293 | return -1; |
