diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-27 15:49:54 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-27 15:49:54 +0100 |
| commit | 1dd3a4d504e98b8d18f236cb1df1fea366ddc36e (patch) | |
| tree | 3beefbc8641ef409597cd2ee4e65f948338bfbbe /src | |
| parent | Spread control: add server RandomSeed GOT diagnostic/override (diff) | |
| download | hl2h-1dd3a4d504e98b8d18f236cb1df1fea366ddc36e.tar.gz hl2h-1dd3a4d504e98b8d18f236cb1df1fea366ddc36e.zip | |
Spread control: override the server FireBullets seed for the local player
The installed 2024 server ignores the client usercmd random_seed and uses its
own shot seed. Force the server's CBaseEntity::FireBullets RandomSeed call for
the local player only:
- hook the server.so RandomSeed GOT slot (R_386_JUMP_SLOT at 0xce1348);
- read the FireBullets `this` from the caller frame and compare it with the
local server player base;
- replace the seed with the precomputed best single/shotgun window and step it
per pellet; NPC shots and everything else pass through.
- client prediction still uses the same chosen usercmd seed, so both sides
agree on the spread pattern.
Diffstat (limited to 'src')
| -rw-r--r-- | src/esp.cpp | 19 | ||||
| -rw-r--r-- | src/hl2h.cpp | 44 | ||||
| -rw-r--r-- | src/hl2h.h | 2 |
3 files changed, 57 insertions, 8 deletions
diff --git a/src/esp.cpp b/src/esp.cpp index 0117d27..ae08258 100644 --- a/src/esp.cpp +++ b/src/esp.cpp | |||
| @@ -1057,6 +1057,7 @@ static bool g_spread_ready = false; | |||
| 1057 | static int g_spread_seed1 = 0; | 1057 | static int g_spread_seed1 = 0; |
| 1058 | static int g_spread_seed8 = 0; // first pellet exact, 7 spread | 1058 | static int g_spread_seed8 = 0; // first pellet exact, 7 spread |
| 1059 | static int g_spread_seed12 = 0; // all 12 spread | 1059 | static int g_spread_seed12 = 0; // all 12 spread |
| 1060 | int g_spread_pellets = 1; | ||
| 1060 | 1061 | ||
| 1061 | static void spread_compute() { | 1062 | static void spread_compute() { |
| 1062 | using SeedFn = void (*)(int); | 1063 | using SeedFn = void (*)(int); |
| @@ -1163,9 +1164,11 @@ bool spread_control_apply(CUserCmd *cmd, void *local_ent) { | |||
| 1163 | return false; | 1164 | return false; |
| 1164 | 1165 | ||
| 1165 | bool shotgun = active_weapon_is_shotgun(local_ent); | 1166 | bool shotgun = active_weapon_is_shotgun(local_ent); |
| 1166 | int seed = g_spread_seed1; | 1167 | int pellets = 1; |
| 1167 | if (shotgun) | 1168 | if (shotgun) |
| 1168 | seed = (cmd->buttons & IN_ATTACK2) ? g_spread_seed12 : g_spread_seed8; | 1169 | pellets = (cmd->buttons & IN_ATTACK2) ? 12 : 8; |
| 1170 | g_spread_pellets = pellets; | ||
| 1171 | int seed = spread_best_seed(pellets); | ||
| 1169 | if (cmd->random_seed == seed) | 1172 | if (cmd->random_seed == seed) |
| 1170 | return false; | 1173 | return false; |
| 1171 | cmd->random_seed = seed; | 1174 | cmd->random_seed = seed; |
| @@ -1173,11 +1176,21 @@ bool spread_control_apply(CUserCmd *cmd, void *local_ent) { | |||
| 1173 | static int logs = 0; | 1176 | static int logs = 0; |
| 1174 | if (logs < 10) { | 1177 | if (logs < 10) { |
| 1175 | logs++; | 1178 | logs++; |
| 1176 | logf("spread: seed=%d (%s)", seed, shotgun ? "shotgun" : "single"); | 1179 | logf("spread: seed=%d pellets=%d", seed, pellets); |
| 1177 | } | 1180 | } |
| 1178 | return true; | 1181 | return true; |
| 1179 | } | 1182 | } |
| 1180 | 1183 | ||
| 1184 | int spread_best_seed(int pellets) { | ||
| 1185 | if (!g_spread_ready) | ||
| 1186 | return 33; | ||
| 1187 | if (pellets >= 12) | ||
| 1188 | return g_spread_seed12; | ||
| 1189 | if (pellets >= 8) | ||
| 1190 | return g_spread_seed8; | ||
| 1191 | return g_spread_seed1; | ||
| 1192 | } | ||
| 1193 | |||
| 1181 | bool triggerbot_apply(CUserCmd *cmd) { | 1194 | bool triggerbot_apply(CUserCmd *cmd) { |
| 1182 | if (!g_iface.enginetrace || !g_iface.entitylist) | 1195 | if (!g_iface.enginetrace || !g_iface.entitylist) |
| 1183 | return false; | 1196 | return false; |
diff --git a/src/hl2h.cpp b/src/hl2h.cpp index 90e0990..c4f8aad 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #include <cstdarg> | 4 | #include <cstdarg> |
| 5 | #include <cstdlib> | 5 | #include <cstdlib> |
| 6 | #include <sys/mman.h> | 6 | #include <sys/mman.h> |
| 7 | #include <time.h> | ||
| 7 | 8 | ||
| 8 | // --------------------------------------------------------------------------- | 9 | // --------------------------------------------------------------------------- |
| 9 | // logging | 10 | // logging |
| @@ -417,24 +418,50 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags, | |||
| 417 | 418 | ||
| 418 | static void (*g_real_random_seed)(int) = nullptr; | 419 | static void (*g_real_random_seed)(int) = nullptr; |
| 419 | static uintptr_t g_srv_base = 0; | 420 | static uintptr_t g_srv_base = 0; |
| 421 | static uintptr_t g_local_server_player = 0; | ||
| 420 | static bool g_seed_hook_installed = false; | 422 | static bool g_seed_hook_installed = false; |
| 421 | 423 | ||
| 422 | static void hkdSrvRandomSeed(int seed) { | 424 | static void hkdSrvRandomSeed(int seed) { |
| 423 | uintptr_t ret = (uintptr_t)__builtin_return_address(0); | 425 | uintptr_t ret = (uintptr_t)__builtin_return_address(0); |
| 424 | long off = (long)(ret - g_srv_base); | 426 | long off = (long)(ret - g_srv_base); |
| 425 | static bool trace = getenv("HL2H_SEED_TRACE") != nullptr; | 427 | static bool trace = getenv("HL2H_SEED_TRACE") != nullptr; |
| 426 | static bool force = getenv("HL2H_SEED_FORCE") != nullptr; | ||
| 427 | static int logs = 0; | 428 | static int logs = 0; |
| 428 | bool bullet_site = (off >= 0x216900 && off <= 0x216930); | 429 | bool bullet_site = (off >= 0x216900 && off <= 0x216930); |
| 430 | |||
| 431 | void *frame = nullptr; | ||
| 432 | __asm__ volatile("movl %%ebp, %0" : "=r"(frame)); | ||
| 433 | if (frame) | ||
| 434 | frame = *(void **)frame; // caller's ebp (CBaseEntity::FireBullets) | ||
| 435 | void *self = frame ? *(void **)((char *)frame + 8) : nullptr; | ||
| 436 | bool local = self && g_local_server_player && | ||
| 437 | (uintptr_t)self == g_local_server_player; | ||
| 438 | |||
| 439 | if (bullet_site && local && feature_active(FEAT_SPREAD) && | ||
| 440 | spread_no_spread_enabled()) { | ||
| 441 | int pellets = g_spread_pellets; | ||
| 442 | if (pellets < 1) | ||
| 443 | pellets = 1; | ||
| 444 | if (pellets > 12) | ||
| 445 | pellets = 12; | ||
| 446 | static int idx = 0; | ||
| 447 | static double last = 0.0; | ||
| 448 | struct timespec ts; | ||
| 449 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
| 450 | double now = (double)ts.tv_sec + (double)ts.tv_nsec * 1e-9; | ||
| 451 | if (now - last > 0.05 || idx >= pellets) | ||
| 452 | idx = 0; | ||
| 453 | last = now; | ||
| 454 | seed = spread_best_seed(pellets) + idx; | ||
| 455 | idx++; | ||
| 456 | } | ||
| 457 | |||
| 429 | if (trace && (bullet_site || logs < 12)) { | 458 | if (trace && (bullet_site || logs < 12)) { |
| 430 | if (logs < 200) { | 459 | if (logs < 200) { |
| 431 | logs++; | 460 | logs++; |
| 432 | logf("srvseed: seed=%d off=0x%lx bullet=%d", seed, off, | 461 | logf("srvseed: seed=%d off=0x%lx bullet=%d self=%p local=%d", seed, off, |
| 433 | (int)bullet_site); | 462 | (int)bullet_site, self, (int)local); |
| 434 | } | 463 | } |
| 435 | } | 464 | } |
| 436 | if (force && bullet_site) | ||
| 437 | seed = 33; | ||
| 438 | if (g_real_random_seed) | 465 | if (g_real_random_seed) |
| 439 | g_real_random_seed(seed); | 466 | g_real_random_seed(seed); |
| 440 | } | 467 | } |
| @@ -491,6 +518,13 @@ static void do_move(int seq, float frametime) { | |||
| 491 | if (!ent) | 518 | if (!ent) |
| 492 | return; | 519 | return; |
| 493 | 520 | ||
| 521 | if (feature_active(FEAT_SPREAD)) { | ||
| 522 | resolve_server_netvars(); | ||
| 523 | void *srv = nullptr; | ||
| 524 | if (server_entity_health(localIdx, nullptr, nullptr, &srv) >= 0 && srv) | ||
| 525 | g_local_server_player = (uintptr_t)srv; | ||
| 526 | } | ||
| 527 | |||
| 494 | CUserCmd *cmd = get_usercmd(seq); | 528 | CUserCmd *cmd = get_usercmd(seq); |
| 495 | if (!cmd) | 529 | if (!cmd) |
| 496 | return; | 530 | return; |
| @@ -151,6 +151,8 @@ bool silent_aim_apply(CUserCmd *cmd); | |||
| 151 | bool melee_weapon_active(void *local_ent); | 151 | bool melee_weapon_active(void *local_ent); |
| 152 | bool melee_aimbot_apply(CUserCmd *cmd); | 152 | bool melee_aimbot_apply(CUserCmd *cmd); |
| 153 | bool spread_control_apply(CUserCmd *cmd, void *local_ent); | 153 | bool spread_control_apply(CUserCmd *cmd, void *local_ent); |
| 154 | int spread_best_seed(int pellets); | ||
| 155 | extern int g_spread_pellets; | ||
| 154 | enum FeatureId { | 156 | enum FeatureId { |
| 155 | FEAT_BHOP = 0, | 157 | FEAT_BHOP = 0, |
| 156 | FEAT_ABH = 1, | 158 | FEAT_ABH = 1, |
