summaryrefslogtreecommitdiffstats
path: root/src/server.cpp
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-06 16:09:55 +0100
committersalmonik <salmonik@tuta.io>2026-11-06 16:09:55 +0100
commit33f5ed9ef58f26c6b3b52cf605dd4311d658a60d (patch)
treea97254872e465c9107b7ef19695ab56bb076d7e2 /src/server.cpp
parentItems: log the usable ammo table once (diff)
downloadhl2h-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 'src/server.cpp')
-rw-r--r--src/server.cpp27
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
69static uintptr_t find_string(const char *s) { 69static 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
278bool 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
278int server_model_index(void *base) { 291int 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;