summaryrefslogtreecommitdiffstats
path: root/src/menu.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/menu.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/menu.cpp')
-rw-r--r--src/menu.cpp99
1 files changed, 40 insertions, 59 deletions
diff --git a/src/menu.cpp b/src/menu.cpp
index 1064365..f3a9bb9 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -19,10 +19,10 @@ void init_conmsg() {
19 if (g_conmsg) 19 if (g_conmsg)
20 return; 20 return;
21 // fallback: dlopen libtier0.so from the engine directory 21 // fallback: dlopen libtier0.so from the engine directory
22 size_t slash = g_engine.path.find_last_of('/'); 22 size_t slash = mod_engine().path.find_last_of('/');
23 if (slash == std::string::npos) 23 if (slash == std::string::npos)
24 return; 24 return;
25 std::string tier0 = g_engine.path.substr(0, slash + 1) + "libtier0.so"; 25 std::string tier0 = mod_engine().path.substr(0, slash + 1) + "libtier0.so";
26 void *h = dlopen(tier0.c_str(), RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); 26 void *h = dlopen(tier0.c_str(), RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL);
27 if (!h) 27 if (!h)
28 h = dlopen(tier0.c_str(), RTLD_LAZY | RTLD_GLOBAL); 28 h = dlopen(tier0.c_str(), RTLD_LAZY | RTLD_GLOBAL);
@@ -640,6 +640,11 @@ static bool config_load() {
640 g_esp_col_friendly[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; 640 g_esp_col_friendly[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v;
641 } 641 }
642 } 642 }
643 // Scalar-only sections ([view], [movement], [chams_*]) intentionally
644 // leave cur == nullptr. A stray active=/bind= line in one of them must
645 // not be dereferenced (hand-edited or corrupt config).
646 if (!cur)
647 continue;
643 if (strcmp(key, "active") == 0) 648 if (strcmp(key, "active") == 0)
644 cur->active = atoi(val) != 0; 649 cur->active = atoi(val) != 0;
645 else if (strcmp(key, "bind") == 0) 650 else if (strcmp(key, "bind") == 0)
@@ -867,6 +872,11 @@ bool feature_active(int feature) {
867 return f->bind == 0 || key_held(f->bind); 872 return f->bind == 0 || key_held(f->bind);
868} 873}
869 874
875bool feature_enabled(int feature) {
876 FeatureState *f = feature_state(feature);
877 return f && f->active;
878}
879
870bool feature_has_bind(int feature) { 880bool feature_has_bind(int feature) {
871 FeatureState *f = feature_state(feature); 881 FeatureState *f = feature_state(feature);
872 return f && f->bind != 0; 882 return f && f->bind != 0;
@@ -924,81 +934,39 @@ bool aimbot_fov_circle() { return g_aim_fov_circle; }
924struct ISurface; 934struct ISurface;
925using HFont = unsigned long; 935using HFont = unsigned long;
926 936
927ISurface *g_surface = nullptr;
928static HFont g_font = 0; 937static HFont g_font = 0;
929static HFont g_font_small = 0; 938static HFont g_font_small = 0;
930 939
931// VGUI_Surface030 vtable slots (confirmed via binary disassembly of vguimatsurface.so): 940// ISurface access lives in overlay.cpp; these wrappers keep the call sites in
932// 8 = PushMakeCurrent(VPANEL, bool useInsets) — reads args at 0xc/0x10(%ebp) 941// this file unchanged.
933// 9 = PopMakeCurrent(VPANEL) — pops the make-current stack at this+0x264/0x270 942static int surf_get_embedded_panel() { return overlay_embedded_panel(); }
934// 10 = DrawSetColor(int r, int g, int b, int a) — stores rgba at this+0x24
935// 12 = DrawFilledRect(int x0, int y0, int x1, int y1) — alpha check at this+0x27
936// 17 = DrawSetTextFont(HFont), 18 = DrawSetTextColor(int r,g,b,a)
937// 20 = DrawSetTextPos(int x, int y), 22 = DrawPrintText(wchar_t*, len, drawType)
938// 66 = CreateFont(), 67 = AddGlyphSetToFont(font, name, tall, weight,
939// blur, scanlines, flags, rangeMin, rangeMax)
940static constexpr int SURF_GetEmbeddedPanel = 6;
941static constexpr int SURF_PushMakeCurrent = 8;
942static constexpr int SURF_PopMakeCurrent = 9;
943static constexpr int SURF_DrawSetColor = 10;
944static constexpr int SURF_DrawFilledRect = 12;
945static constexpr int SURF_DrawSetTextFont = 17;
946static constexpr int SURF_DrawSetTextColor = 18;
947static constexpr int SURF_DrawSetTextPos = 20;
948static constexpr int SURF_DrawPrintText = 22;
949static constexpr int SURF_CreateFont = 66;
950static constexpr int SURF_AddGlyphSetToFont = 67;
951
952static int surf_get_embedded_panel() {
953 using Fn = int (*)(void *);
954 return vfunc<Fn>(g_surface, SURF_GetEmbeddedPanel)(g_surface);
955}
956static void surf_push_make_current(int panel, bool use_insets) { 943static void surf_push_make_current(int panel, bool use_insets) {
957 using Fn = void (*)(void *, int, bool); 944 overlay_push_make_current(panel, use_insets);
958 vfunc<Fn>(g_surface, SURF_PushMakeCurrent)(g_surface, panel, use_insets);
959} 945}
960static void surf_pop_make_current(int panel) { 946static void surf_pop_make_current(int panel) {
961 using Fn = void (*)(void *, int); 947 overlay_pop_make_current(panel);
962 vfunc<Fn>(g_surface, SURF_PopMakeCurrent)(g_surface, panel);
963} 948}
964static void surf_draw_set_color(int r, int g, int b, int a) { 949static void surf_draw_set_color(int r, int g, int b, int a) {
965 using Fn = void (*)(void *, int, int, int, int); 950 overlay_color(r, g, b, a);
966 vfunc<Fn>(g_surface, SURF_DrawSetColor)(g_surface, r, g, b, a);
967} 951}
968static void surf_draw_filled_rect(int x0, int y0, int x1, int y1) { 952static void surf_draw_filled_rect(int x0, int y0, int x1, int y1) {
969 using Fn = void (*)(void *, int, int, int, int); 953 overlay_fill(x0, y0, x1, y1);
970 vfunc<Fn>(g_surface, SURF_DrawFilledRect)(g_surface, x0, y0, x1, y1);
971} 954}
972static void surf_draw_set_text_color(int r, int g, int b, int a) { 955static void surf_draw_set_text_color(int r, int g, int b, int a) {
973 using Fn = void (*)(void *, int, int, int, int); 956 overlay_text_color(r, g, b, a);
974 vfunc<Fn>(g_surface, SURF_DrawSetTextColor)(g_surface, r, g, b, a);
975}
976static void surf_draw_set_text_font(HFont f) {
977 using Fn = void (*)(void *, HFont);
978 vfunc<Fn>(g_surface, SURF_DrawSetTextFont)(g_surface, f);
979}
980static void surf_draw_set_text_pos(int x, int y) {
981 using Fn = void (*)(void *, int, int);
982 vfunc<Fn>(g_surface, SURF_DrawSetTextPos)(g_surface, x, y);
983} 957}
958static void surf_draw_set_text_font(HFont f) { overlay_text_font(f); }
959static void surf_draw_set_text_pos(int x, int y) { overlay_text_pos(x, y); }
984static void surf_draw_print_text(const wchar_t *text, int count) { 960static void surf_draw_print_text(const wchar_t *text, int count) {
985 using Fn = void (*)(void *, const wchar_t *, int, int); 961 overlay_print_text(text, count);
986 vfunc<Fn>(g_surface, SURF_DrawPrintText)(g_surface, text, count, 0); // 0 = FONT_DRAW_DEFAULT
987}
988static HFont surf_create_font() {
989 using Fn = HFont (*)(void *);
990 return vfunc<Fn>(g_surface, SURF_CreateFont)(g_surface);
991} 962}
963static HFont surf_create_font() { return overlay_create_font(); }
992static bool surf_set_font_glyph_set(HFont f, const char *name, int tall, 964static bool surf_set_font_glyph_set(HFont f, const char *name, int tall,
993 int weight, int blur, int scanlines, 965 int weight, int blur, int scanlines,
994 int flags, int range_min = 0, 966 int flags, int range_min = 0,
995 int range_max = 0) { 967 int range_max = 0) {
996 using Fn = bool (*)(void *, HFont, const char *, int, int, int, int, int, int, 968 return overlay_add_glyph_set(f, name, tall, weight, blur, scanlines, flags,
997 int); 969 range_min, range_max);
998 return vfunc<Fn>(g_surface, SURF_AddGlyphSetToFont)(g_surface, f, name, tall,
999 weight, blur, scanlines,
1000 flags, range_min,
1001 range_max);
1002} 970}
1003 971
1004// wide string conversion for DrawPrintText 972// wide string conversion for DrawPrintText
@@ -1071,6 +1039,11 @@ void fullbright_update() {
1071// (see hkdDrawPrintText hook in hl2h.cpp): we wrap our draws in 1039// (see hkdDrawPrintText hook in hl2h.cpp): we wrap our draws in
1072// PushMakeCurrent/PopMakeCurrent so they are submitted with a clean clip. 1040// PushMakeCurrent/PopMakeCurrent so they are submitted with a clean clip.
1073void outer_draw() { 1041void outer_draw() {
1042 // PaintTraverseEx runs for every VGUI panel and DrawPrintText for every text
1043 // draw, so collapse them to one draw per engine frame (see overlay.h).
1044 if (!overlay_should_draw())
1045 return;
1046
1074 poll_keyboard(); 1047 poll_keyboard();
1075 fullbright_update(); 1048 fullbright_update();
1076 world_tint_update(); 1049 world_tint_update();
@@ -1083,10 +1056,16 @@ void outer_draw() {
1083 bool esp_on = feature_active(FEAT_ESP); 1056 bool esp_on = feature_active(FEAT_ESP);
1084 bool items_on = feature_active(FEAT_ITEM_ESP); 1057 bool items_on = feature_active(FEAT_ITEM_ESP);
1085 bool projs_on = feature_active(FEAT_PROJECTILES); 1058 bool projs_on = feature_active(FEAT_PROJECTILES);
1059 // The FOV circle belongs to the aim features, not to ESP, and it is a visual
1060 // aid: use the enable toggle rather than the bind, otherwise it only appears
1061 // while the aim key is held (default bind is mouse5).
1062 bool aim_on = (feature_enabled(FEAT_AIMBOT) ||
1063 feature_enabled(FEAT_SILENTAIM)) &&
1064 aimbot_fov_circle();
1086 bool toasts = gui_toasts_active(); 1065 bool toasts = gui_toasts_active();
1087 if (!g_surface || 1066 if (!g_surface ||
1088 (!gui_is_open() && !draw_test && !esp_on && !items_on && !projs_on && 1067 (!gui_is_open() && !draw_test && !esp_on && !items_on && !projs_on &&
1089 !toasts)) 1068 !aim_on && !toasts))
1090 return; 1069 return;
1091 1070
1092 // Level/menu -> game transitions can invalidate the VGUI font handle. 1071 // Level/menu -> game transitions can invalidate the VGUI font handle.
@@ -1147,6 +1126,8 @@ void outer_draw() {
1147 if (!panel) 1126 if (!panel)
1148 return; 1127 return;
1149 surf_push_make_current(panel, false); 1128 surf_push_make_current(panel, false);
1129 if (aim_on && overlay_font)
1130 esp_draw_fov_circle();
1150 if (esp_on && overlay_font) 1131 if (esp_on && overlay_font)
1151 esp_draw(); 1132 esp_draw();
1152 if (items_on && overlay_font) 1133 if (items_on && overlay_font)