summaryrefslogtreecommitdiffstats
path: root/src/hl2h.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/hl2h.cpp50
1 files changed, 35 insertions, 15 deletions
diff --git a/src/hl2h.cpp b/src/hl2h.cpp
index 28b2995..7ce35d3 100644
--- a/src/hl2h.cpp
+++ b/src/hl2h.cpp
@@ -471,11 +471,12 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags,
471// server RandomSeed diagnostic / override 471// server RandomSeed diagnostic / override
472// --------------------------------------------------------------------------- 472// ---------------------------------------------------------------------------
473// 473//
474// The installed 2024 server build is stripped and its usercmd/server-random-seed 474// The server is a stripped build whose usercmd/random-seed handling differs
475// handling differs from the 2013 SDK source. This hook is installed on the 475// from the 2013 SDK source. Hook the server.so PLT slot for RandomSeed so we
476// server.so GOT slot for RandomSeed (R_386_JUMP_SLOT at vaddr 0xce1348) so we 476// can force the chosen spread seed. The GOT offset is read from the loaded
477// can see which seed FireBullets actually uses and optionally force the chosen 477// module's relocation table at runtime (module_jump_slot): hardcoding it broke
478// spread seed. Disabled unless HL2H_SEED_TRACE / HL2H_SEED_FORCE are set. 478// when Steam switched to the hl2_complete/server.so branch (0xce1348 ->
479// 0xce5348).
479 480
480static void (*g_real_random_seed)(int) = nullptr; 481static void (*g_real_random_seed)(int) = nullptr;
481static uintptr_t g_srv_base = 0; 482static uintptr_t g_srv_base = 0;
@@ -487,8 +488,10 @@ static void hkdSrvRandomSeed(int seed) {
487 long off = (long)(ret - g_srv_base); 488 long off = (long)(ret - g_srv_base);
488 static bool trace = getenv("HL2H_SEED_TRACE") != nullptr; 489 static bool trace = getenv("HL2H_SEED_TRACE") != nullptr;
489 static int logs = 0; 490 static int logs = 0;
490 bool bullet_site = (off >= 0x216900 && off <= 0x216930);
491 491
492 // The caller's first argument is `this`. Only FireBullets is called on the
493 // player entity; the CUniformRandomStream wrappers and the constructor-time
494 // RandomSeed(0) reset have a different object there.
492 void *frame = nullptr; 495 void *frame = nullptr;
493 __asm__ volatile("movl %%ebp, %0" : "=r"(frame)); 496 __asm__ volatile("movl %%ebp, %0" : "=r"(frame));
494 if (frame) 497 if (frame)
@@ -497,7 +500,7 @@ static void hkdSrvRandomSeed(int seed) {
497 bool local = self && g_local_server_player && 500 bool local = self && g_local_server_player &&
498 (uintptr_t)self == g_local_server_player; 501 (uintptr_t)self == g_local_server_player;
499 502
500 if (bullet_site && local && feature_active(FEAT_SPREAD) && 503 if (local && g_server.contains(ret) && feature_active(FEAT_SPREAD) &&
501 spread_no_spread_enabled()) { 504 spread_no_spread_enabled()) {
502 int pellets = g_spread_pellets; 505 int pellets = g_spread_pellets;
503 if (pellets < 1) 506 if (pellets < 1)
@@ -516,12 +519,10 @@ static void hkdSrvRandomSeed(int seed) {
516 idx++; 519 idx++;
517 } 520 }
518 521
519 if (trace && (bullet_site || logs < 12)) { 522 if (trace && logs < 200) {
520 if (logs < 200) { 523 logs++;
521 logs++; 524 logd("srvseed: seed=%d off=0x%lx self=%p local=%d", seed, off, self,
522 logd("srvseed: seed=%d off=0x%lx bullet=%d self=%p local=%d", seed, off, 525 (int)local);
523 (int)bullet_site, self, (int)local);
524 }
525 } 526 }
526 if (g_real_random_seed) 527 if (g_real_random_seed)
527 g_real_random_seed(seed); 528 g_real_random_seed(seed);
@@ -532,9 +533,28 @@ static void install_seed_hook() {
532 if (g_seed_hook_installed || failed || !g_server.valid()) 533 if (g_seed_hook_installed || failed || !g_server.valid())
533 return; 534 return;
534 g_srv_base = g_server.ranges[0].lo; 535 g_srv_base = g_server.ranges[0].lo;
535 uintptr_t slot = g_srv_base + 0xce1348; // R_386_JUMP_SLOT RandomSeed 536 uintptr_t reloc = 0, bias = 0;
536 if (!g_server.contains(slot)) 537 uintptr_t slot = 0;
538 if (module_jump_slot(g_server.path.c_str(), "RandomSeed", &reloc, &bias)) {
539 slot = g_srv_base + reloc - bias;
540 logf("seed hook: RandomSeed GOT reloc=%#lx bias=%#lx -> %#lx",
541 (unsigned long)reloc, (unsigned long)bias, (unsigned long)slot);
542 } else {
543 // ELF parse failed (deleted/unreadable module); try the builds we know.
544 static const uintptr_t known[] = {0xce5348, 0xce1348};
545 for (size_t i = 0; i < sizeof(known) / sizeof(known[0]) && !slot; i++) {
546 uintptr_t cand = g_srv_base + known[i];
547 if (g_server.contains(cand))
548 slot = cand;
549 }
550 logf("seed hook: ELF reloc scan failed, fallback slot=%#lx",
551 (unsigned long)slot);
552 }
553 if (!slot || !g_server.contains(slot)) {
554 failed = true;
555 logf("seed hook: no writable RandomSeed slot");
537 return; 556 return;
557 }
538 g_real_random_seed = 558 g_real_random_seed =
539 (void (*)(int))dlsym(RTLD_NEXT, "RandomSeed"); 559 (void (*)(int))dlsym(RTLD_NEXT, "RandomSeed");
540 if (!g_real_random_seed) 560 if (!g_real_random_seed)