summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-27 15:49:54 +0100
committersalmonik <salmonik@tuta.io>2026-10-27 15:49:54 +0100
commit1dd3a4d504e98b8d18f236cb1df1fea366ddc36e (patch)
tree3beefbc8641ef409597cd2ee4e65f948338bfbbe /src
parentSpread control: add server RandomSeed GOT diagnostic/override (diff)
downloadhl2h-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.cpp19
-rw-r--r--src/hl2h.cpp44
-rw-r--r--src/hl2h.h2
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;
1057static int g_spread_seed1 = 0; 1057static int g_spread_seed1 = 0;
1058static int g_spread_seed8 = 0; // first pellet exact, 7 spread 1058static int g_spread_seed8 = 0; // first pellet exact, 7 spread
1059static int g_spread_seed12 = 0; // all 12 spread 1059static int g_spread_seed12 = 0; // all 12 spread
1060int g_spread_pellets = 1;
1060 1061
1061static void spread_compute() { 1062static 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
1184int 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
1181bool triggerbot_apply(CUserCmd *cmd) { 1194bool 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
418static void (*g_real_random_seed)(int) = nullptr; 419static void (*g_real_random_seed)(int) = nullptr;
419static uintptr_t g_srv_base = 0; 420static uintptr_t g_srv_base = 0;
421static uintptr_t g_local_server_player = 0;
420static bool g_seed_hook_installed = false; 422static bool g_seed_hook_installed = false;
421 423
422static void hkdSrvRandomSeed(int seed) { 424static 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;
diff --git a/src/hl2h.h b/src/hl2h.h
index ef8e8d6..285b591 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -151,6 +151,8 @@ bool silent_aim_apply(CUserCmd *cmd);
151bool melee_weapon_active(void *local_ent); 151bool melee_weapon_active(void *local_ent);
152bool melee_aimbot_apply(CUserCmd *cmd); 152bool melee_aimbot_apply(CUserCmd *cmd);
153bool spread_control_apply(CUserCmd *cmd, void *local_ent); 153bool spread_control_apply(CUserCmd *cmd, void *local_ent);
154int spread_best_seed(int pellets);
155extern int g_spread_pellets;
154enum FeatureId { 156enum FeatureId {
155 FEAT_BHOP = 0, 157 FEAT_BHOP = 0,
156 FEAT_ABH = 1, 158 FEAT_ABH = 1,