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/world.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/world.cpp | 45 |
1 files changed, 42 insertions, 3 deletions
diff --git a/src/world.cpp b/src/world.cpp index ea186b4..a0576ab 100644 --- a/src/world.cpp +++ b/src/world.cpp | |||
| @@ -633,6 +633,10 @@ bool load_png_rgba(const char *path, std::vector<unsigned char> &pixels, | |||
| 633 | } | 633 | } |
| 634 | 634 | ||
| 635 | std::vector<unsigned char> g_sky_pixels; | 635 | std::vector<unsigned char> g_sky_pixels; |
| 636 | // Pristine copy of the decoded PNG. HDR compensation is applied to | ||
| 637 | // g_sky_pixels in place, so without this the linearised/darkened bytes bled | ||
| 638 | // into the next map when it used the LDR sky shader. | ||
| 639 | std::vector<unsigned char> g_sky_pixels_src; | ||
| 636 | int g_sky_px_w = 0, g_sky_px_h = 0; | 640 | int g_sky_px_w = 0, g_sky_px_h = 0; |
| 637 | 641 | ||
| 638 | // ITextureRegenerator has two virtuals: RegenerateTextureBits then Release. | 642 | // ITextureRegenerator has two virtuals: RegenerateTextureBits then Release. |
| @@ -717,6 +721,28 @@ bool is_sky_material(const char *name, const char *group) { | |||
| 717 | strncmp(name, "skybox/", 7) == 0; | 721 | strncmp(name, "skybox/", 7) == 0; |
| 718 | } | 722 | } |
| 719 | 723 | ||
| 724 | // ITexture reference counting (material system header order, matching the | ||
| 725 | // slots already used here: SetTextureRegenerator = 12, Download = 13): | ||
| 726 | // 10 = IncrementReferenceCount, 11 = DecrementReferenceCount | ||
| 727 | // The engine owns the original textures; holding a reference keeps them valid | ||
| 728 | // between "capture" and "restore" instead of leaving a dangling pointer. | ||
| 729 | static void texture_addref(void *tex) { | ||
| 730 | if (!tex) | ||
| 731 | return; | ||
| 732 | using Fn = void (*)(void *); | ||
| 733 | vfunc<Fn>(tex, 10)(tex); | ||
| 734 | } | ||
| 735 | |||
| 736 | void release_sky_originals() { | ||
| 737 | for (auto &r : g_sky_orig) { | ||
| 738 | if (!r.texture) | ||
| 739 | continue; | ||
| 740 | using Fn = void (*)(void *); | ||
| 741 | vfunc<Fn>(r.texture, 11)(r.texture); | ||
| 742 | } | ||
| 743 | g_sky_orig.clear(); | ||
| 744 | } | ||
| 745 | |||
| 720 | void record_orig(const char *material, const char *var, const char *value, | 746 | void record_orig(const char *material, const char *var, const char *value, |
| 721 | void *texture) { | 747 | void *texture) { |
| 722 | if (!material || !var) | 748 | if (!material || !var) |
| @@ -725,6 +751,7 @@ void record_orig(const char *material, const char *var, const char *value, | |||
| 725 | if (r.material == material && r.var == var) | 751 | if (r.material == material && r.var == var) |
| 726 | return; | 752 | return; |
| 727 | } | 753 | } |
| 754 | texture_addref(texture); | ||
| 728 | g_sky_orig.push_back( | 755 | g_sky_orig.push_back( |
| 729 | {material, var, value ? value : "", texture}); | 756 | {material, var, value ? value : "", texture}); |
| 730 | } | 757 | } |
| @@ -750,6 +777,10 @@ int apply_sky_texture() { | |||
| 750 | // where the materials are definitely loaded, and re-upload if needed. | 777 | // where the materials are definitely loaded, and re-upload if needed. |
| 751 | bool hdr = sky_uses_hdr_shader(); | 778 | bool hdr = sky_uses_hdr_shader(); |
| 752 | if (hdr && !g_sky_compensated && !g_sky_pixels.empty()) { | 779 | if (hdr && !g_sky_compensated && !g_sky_pixels.empty()) { |
| 780 | // Always start from the untouched decode so repeated HDR passes cannot | ||
| 781 | // compound the gamma conversion. | ||
| 782 | if (!g_sky_pixels_src.empty()) | ||
| 783 | g_sky_pixels = g_sky_pixels_src; | ||
| 753 | for (size_t i = 0; i + 3 < g_sky_pixels.size(); i += 4) { | 784 | for (size_t i = 0; i + 3 < g_sky_pixels.size(); i += 4) { |
| 754 | for (int c = 0; c < 3; c++) { | 785 | for (int c = 0; c < 3; c++) { |
| 755 | float v = (float)g_sky_pixels[i + (size_t)c] / 255.0f; | 786 | float v = (float)g_sky_pixels[i + (size_t)c] / 255.0f; |
| @@ -822,7 +853,7 @@ void restore_sky_texture() { | |||
| 822 | } | 853 | } |
| 823 | void *ms = material_system(); | 854 | void *ms = material_system(); |
| 824 | if (!ms) { | 855 | if (!ms) { |
| 825 | g_sky_orig.clear(); | 856 | release_sky_originals(); |
| 826 | g_sky_applied = false; | 857 | g_sky_applied = false; |
| 827 | return; | 858 | return; |
| 828 | } | 859 | } |
| @@ -833,7 +864,7 @@ void restore_sky_texture() { | |||
| 833 | auto count = vfunc<MatCalls::CountFn>(ms, MATSYS_GetNumMaterials); | 864 | auto count = vfunc<MatCalls::CountFn>(ms, MATSYS_GetNumMaterials); |
| 834 | int total = count(ms); | 865 | int total = count(ms); |
| 835 | if (total <= 0 || total > 200000) { | 866 | if (total <= 0 || total > 200000) { |
| 836 | g_sky_orig.clear(); | 867 | release_sky_originals(); |
| 837 | g_sky_applied = false; | 868 | g_sky_applied = false; |
| 838 | return; | 869 | return; |
| 839 | } | 870 | } |
| @@ -866,7 +897,7 @@ void restore_sky_texture() { | |||
| 866 | vfunc<SetStrFn>(var, 5)(var, r.value.c_str()); | 897 | vfunc<SetStrFn>(var, 5)(var, r.value.c_str()); |
| 867 | } | 898 | } |
| 868 | } | 899 | } |
| 869 | g_sky_orig.clear(); | 900 | release_sky_originals(); |
| 870 | g_sky_applied = false; | 901 | g_sky_applied = false; |
| 871 | } | 902 | } |
| 872 | 903 | ||
| @@ -925,6 +956,7 @@ void load_sky_texture() { | |||
| 925 | return; | 956 | return; |
| 926 | } | 957 | } |
| 927 | g_sky_pixels.swap(pixels); | 958 | g_sky_pixels.swap(pixels); |
| 959 | g_sky_pixels_src = g_sky_pixels; | ||
| 928 | g_sky_px_w = w; | 960 | g_sky_px_w = w; |
| 929 | g_sky_px_h = h; | 961 | g_sky_px_h = h; |
| 930 | 962 | ||
| @@ -960,6 +992,13 @@ void skybox_invalidate() { | |||
| 960 | 992 | ||
| 961 | void skybox_reload_image() { | 993 | void skybox_reload_image() { |
| 962 | restore_sky_texture(); | 994 | restore_sky_texture(); |
| 995 | // Detach the regenerator before dropping the handle: the old texture may | ||
| 996 | // outlive it and must not call back into a regenerator whose pixels were | ||
| 997 | // replaced. | ||
| 998 | if (g_sky_tex) { | ||
| 999 | using SetRegenFn = void (*)(void *, void *); | ||
| 1000 | vfunc<SetRegenFn>(g_sky_tex, 12)(g_sky_tex, nullptr); | ||
| 1001 | } | ||
| 963 | g_sky_tex = nullptr; | 1002 | g_sky_tex = nullptr; |
| 964 | g_sky_tex_tried = false; | 1003 | g_sky_tex_tried = false; |
| 965 | g_sky_reload = true; | 1004 | g_sky_reload = true; |
