summaryrefslogtreecommitdiffstats
path: root/src/esp.cpp
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/esp.cpp
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/esp.cpp')
-rw-r--r--src/esp.cpp19
1 files changed, 16 insertions, 3 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;