diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-27 15:32:26 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-27 15:32:26 +0100 |
| commit | 902507c1173a03a04fc49cfe421bc3c3fb49d8fb (patch) | |
| tree | 433cae8017464b2fb8f7ed92be8d894db521be38 /src | |
| parent | Spread control: client-seed no-spread (diff) | |
| download | hl2h-902507c1173a03a04fc49cfe421bc3c3fb49d8fb.tar.gz hl2h-902507c1173a03a04fc49cfe421bc3c3fb49d8fb.zip | |
Spread control: set random_seed after CInput::CreateMove
CInput::CreateMove overwrites cmd->random_seed with MD5_PseudoRandom after
CHLClient::CreateMove returns, so the seed set from the client-mode hook was
being discarded. Hook the IInput vtable slot 3 (CInput::CreateMove), call the
original, then apply the spread seed and update the verified command copy and
CRC. This also keeps all aim/movement edits since the original copies the
command into the verified table.
Diffstat (limited to 'src')
| -rw-r--r-- | src/cinput.cpp | 58 | ||||
| -rw-r--r-- | src/hl2h.cpp | 3 |
2 files changed, 58 insertions, 3 deletions
diff --git a/src/cinput.cpp b/src/cinput.cpp index f4ba322..a75cc4f 100644 --- a/src/cinput.cpp +++ b/src/cinput.cpp | |||
| @@ -74,6 +74,63 @@ void fix_verified_crc(int seq, uint32_t crc) { | |||
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | // --------------------------------------------------------------------------- | 76 | // --------------------------------------------------------------------------- |
| 77 | // CInput::CreateMove hook | ||
| 78 | // --------------------------------------------------------------------------- | ||
| 79 | // | ||
| 80 | // The CHLClient::CreateMove hook runs *inside* CInput::CreateMove. After it | ||
| 81 | // returns, CInput::CreateMove overwrites cmd->random_seed with | ||
| 82 | // MD5_PseudoRandom(sequence_number), then copies the command into the verified | ||
| 83 | // command table. To choose the bullet-spread seed we must hook CInput itself | ||
| 84 | // and run after that assignment. | ||
| 85 | |||
| 86 | using InputCreateMoveFn = void (*)(void *, int, float, bool); | ||
| 87 | static InputCreateMoveFn oInputCreateMove = nullptr; | ||
| 88 | |||
| 89 | static void hkdInputCreateMove(void *self, int seq, float frametime, | ||
| 90 | bool active) { | ||
| 91 | if (oInputCreateMove) | ||
| 92 | oInputCreateMove(self, seq, frametime, active); | ||
| 93 | |||
| 94 | CUserCmd *cmd = get_usercmd(seq); | ||
| 95 | if (!cmd) | ||
| 96 | return; | ||
| 97 | int local = engine_get_local_player(); | ||
| 98 | if (local <= 0) | ||
| 99 | return; | ||
| 100 | void *ent = entitylist_get(local); | ||
| 101 | if (!ent) | ||
| 102 | return; | ||
| 103 | if (!spread_control_apply(cmd, ent)) | ||
| 104 | return; | ||
| 105 | |||
| 106 | // Keep the verified command copy and its CRC in sync with the modified | ||
| 107 | // random_seed. CInput::CreateMove already copied all other (aim/move) fields. | ||
| 108 | uintptr_t verified = *(uintptr_t *)(g_input + CMDOFF_M_PVERIFIED); | ||
| 109 | if (verified) { | ||
| 110 | CUserCmd *vc = | ||
| 111 | (CUserCmd *)(verified + (size_t)(seq % 90) * VERIFIED_STRIDE); | ||
| 112 | vc->random_seed = cmd->random_seed; | ||
| 113 | } | ||
| 114 | fix_verified_crc(seq, usercmd_checksum(cmd)); | ||
| 115 | } | ||
| 116 | |||
| 117 | static bool install_input_hook() { | ||
| 118 | if (!g_input) | ||
| 119 | return false; | ||
| 120 | void **vt = VTABLE((void *)g_input); | ||
| 121 | if (!vt) | ||
| 122 | return false; | ||
| 123 | constexpr size_t N = 64; | ||
| 124 | void **nvt = new void *[N]; | ||
| 125 | memcpy(nvt, vt, N * sizeof(void *)); | ||
| 126 | oInputCreateMove = (InputCreateMoveFn)vt[3]; | ||
| 127 | nvt[3] = (void *)&hkdInputCreateMove; | ||
| 128 | VTABLE((void *)g_input) = nvt; | ||
| 129 | logf("CInput CreateMove hooked: slot 3 orig=%p", (void *)oInputCreateMove); | ||
| 130 | return true; | ||
| 131 | } | ||
| 132 | |||
| 133 | // --------------------------------------------------------------------------- | ||
| 77 | // CInput discovery: byte-scan of CHLClient::CreateMove prologue | 134 | // CInput discovery: byte-scan of CHLClient::CreateMove prologue |
| 78 | // --------------------------------------------------------------------------- | 135 | // --------------------------------------------------------------------------- |
| 79 | 136 | ||
| @@ -135,6 +192,7 @@ bool find_input_global() { | |||
| 135 | logf("CInput* = %#lx (ptr var %#lx, vfunc[3] %#lx)", (unsigned long)input, | 192 | logf("CInput* = %#lx (ptr var %#lx, vfunc[3] %#lx)", (unsigned long)input, |
| 136 | (unsigned long)var, (unsigned long)f3); | 193 | (unsigned long)var, (unsigned long)f3); |
| 137 | g_input = input; | 194 | g_input = input; |
| 195 | install_input_hook(); | ||
| 138 | return true; | 196 | return true; |
| 139 | } | 197 | } |
| 140 | } | 198 | } |
diff --git a/src/hl2h.cpp b/src/hl2h.cpp index dddcbdc..ee2e990 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp | |||
| @@ -532,9 +532,6 @@ static void do_move(int seq, float frametime) { | |||
| 532 | note_modified(cmd, flags, vel); | 532 | note_modified(cmd, flags, vel); |
| 533 | } | 533 | } |
| 534 | } | 534 | } |
| 535 | |||
| 536 | if (spread_control_apply(cmd, ent)) | ||
| 537 | note_modified(cmd, flags, vel); | ||
| 538 | } | 535 | } |
| 539 | 536 | ||
| 540 | // --------------------------------------------------------------------------- | 537 | // --------------------------------------------------------------------------- |
