From aa096fe6886681d3338573cd3af779b57a5b56ab Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 30 Aug 2026 03:32:59 +0200 Subject: init --- src/hl2h.cpp | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 src/hl2h.cpp (limited to 'src/hl2h.cpp') diff --git a/src/hl2h.cpp b/src/hl2h.cpp new file mode 100644 index 0000000..0261ff2 --- /dev/null +++ b/src/hl2h.cpp @@ -0,0 +1,307 @@ +// hl2h.cpp — core: logging, vector math, globals, feature logic, setup +#include "hl2h.h" +#include +#include + +// --------------------------------------------------------------------------- +// logging +// --------------------------------------------------------------------------- + +void logf(const char *fmt, ...) { + va_list ap, ap2; + va_start(ap, fmt); + va_copy(ap2, ap); + fprintf(stderr, "hl2hake: "); + vfprintf(stderr, fmt, ap2); + fprintf(stderr, "\n"); + fflush(stderr); + va_end(ap2); + if (FILE *f = fopen("/tmp/hl2hake.log", "a")) { + fprintf(f, "hl2hake: "); + vfprintf(f, fmt, ap); + fprintf(f, "\n"); + fclose(f); + } + va_end(ap); +} + +// --------------------------------------------------------------------------- +// globals +// --------------------------------------------------------------------------- + +Module g_client, g_engine; +GameInterfaces g_iface; +NetVars g_net; +uintptr_t g_input = 0; + +// --------------------------------------------------------------------------- +// vector math +// --------------------------------------------------------------------------- + +static constexpr float PI_F = 3.14159265358979323846f; + +QAngle vector_angles(const Vector &forward) { + QAngle a{}; + a.z = 0; + if (forward.y == 0 && forward.x == 0) { + a.y = 0; + a.x = (forward.z > 0) ? -90.f : 90.f; + } else { + a.y = atan2f(forward.y, forward.x) * 180.f / PI_F; + if (a.y < 0) + a.y += 360.f; + a.x = atan2f(-forward.z, + sqrtf(forward.x * forward.x + forward.y * forward.y)) * + 180.f / PI_F; + if (a.x < 0) + a.x += 360.f; + } + return a; +} + +Vector angle_forward_vector(const QAngle &angles) { + auto DEG2RAD = [](float x) { return x * (PI_F / 180.f); }; + float sp = sinf(DEG2RAD(angles.x)), cp = cosf(DEG2RAD(angles.x)); + float sy = sinf(DEG2RAD(angles.y)), cy = cosf(DEG2RAD(angles.y)); + return {cp * cy, cp * sy, -sp}; +} + +float getfov(const QAngle &my_view, const QAngle &aim_ang) { + Vector aim = angle_forward_vector(my_view); + Vector ang = angle_forward_vector(aim_ang); + float len = sqrtf(aim.x * aim.x + aim.y * aim.y + aim.z * aim.z); + if (len <= 0) + return 0; + float dot = (aim.x * ang.x + aim.y * ang.y + aim.z * ang.z) / len; + if (dot > 1) + dot = 1; + if (dot < -1) + dot = -1; + return acosf(dot) * (180.f / PI_F); +} + +// --------------------------------------------------------------------------- +// feature logic (bhop, ABH, climb) +// --------------------------------------------------------------------------- + +static int g_modified_logged = 0; + +void note_modified(const CUserCmd *cmd, int flags, const float *vel) { + // recompute usercmd CRC after modification + fix_verified_crc(cmd->command_number, usercmd_checksum(cmd)); + + if (g_modified_logged < 5) { + logf("cmd modified: seq=%d flags=0x%X buttons=0x%X fw=%.2f " + "vel=(%.1f,%.1f,%.1f)", + cmd->command_number, flags, cmd->buttons, cmd->forwardmove, vel[0], + vel[1], vel[2]); + g_modified_logged++; + } +} + +// menu state (defined in menu.cpp, referenced here) + +static bool g_logged_local = false; + +static int engine_get_local_player() { + using Fn = int (*)(void *); + return vfunc(g_iface.engine, 12)(g_iface.engine); +} + +static void *entitylist_get(int idx) { + using Fn = void *(*)(void *, int); + return vfunc(g_iface.entitylist, 3)(g_iface.entitylist, idx); +} + + + + +static void do_move(int seq) { + if (!g_input || !g_net.ok()) + return; + + int localIdx = engine_get_local_player(); + if (localIdx <= 0 || localIdx > 64) + return; + if (!g_logged_local) { + logf("local player index %d (in-game)", localIdx); + g_logged_local = true; + } + + void *ent = entitylist_get(localIdx); + if (!ent) + return; + + CUserCmd *cmd = get_usercmd(seq); + if (!cmd) + return; + + uintptr_t e = (uintptr_t)ent; + uint8_t movetype = *(uint8_t *)(e + g_net.movetype); + int flags = *(int *)(e + g_net.flags); + bool onground = (flags & FL_ONGROUND) != 0; + bool inwater = (flags & FL_INWATER) != 0; + + if (movetype == MOVETYPE_NOCLIP || movetype == MOVETYPE_LADDER) + return; + + static bool lastground = false; + + if (middle_mouse_down()) { + if (cmd->command_number % 2) { + cmd->buttons &= ~IN_JUMP; + cmd->buttons |= IN_USE; + } else { + cmd->buttons &= ~IN_USE; + cmd->buttons |= IN_JUMP; + } + note_modified(cmd, flags, (float *)(e + g_net.velocity)); + } else if (cmd->buttons & IN_JUMP) { + if (inwater && (cmd->buttons & IN_DUCK)) { + lastground = onground; + return; + } + const float *vel = (const float *)(e + g_net.velocity); + float speed = sqrtf(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]); + bool moving = speed > 0.1f; + bool backmove = false; + if (moving) { + QAngle moveang = vector_angles(Vector{vel[0], vel[1], vel[2]}); + moveang.x = 0.f; + QAngle viewang{cmd->viewangles[0], cmd->viewangles[1], + cmd->viewangles[2]}; + viewang.x = 0.f; + backmove = getfov(viewang, moveang) > 90.f; + } + if (!onground) { + lastground = false; + cmd->buttons &= ~IN_JUMP; + note_modified(cmd, flags, vel); + } else if (!lastground) { + lastground = true; + if (!backmove) + cmd->forwardmove = -0.1f; + note_modified(cmd, flags, vel); + } + } +} + +// --------------------------------------------------------------------------- +// CreateMove hook (cdecl — game virtuals use cdecl, not __thiscall) +// --------------------------------------------------------------------------- + +using CreateMoveFn = bool (*)(void *, int, float, bool); +static CreateMoveFn oCreateMove = nullptr; + +static bool hkdCreateMove(void *self, int seq, float frametime, bool active) { + bool ret = oCreateMove(self, seq, frametime, active); + do_move(seq); + return ret; +} + +static void install_hook() { + void **vt = VTABLE(g_iface.client); + constexpr size_t SLOTS = 256; + void **newvt = new void *[SLOTS]; + memcpy(newvt, vt, SLOTS * sizeof(void *)); + oCreateMove = (CreateMoveFn)vt[21]; + newvt[21] = (void *)&hkdCreateMove; + VTABLE(g_iface.client) = newvt; + logf("hook installed: CreateMove %p -> %p (orig %p)", vt[21], + (void *)&hkdCreateMove, (void *)oCreateMove); +} + +// --------------------------------------------------------------------------- +// setup thread +// --------------------------------------------------------------------------- + +extern void find_modules(); +extern void resolve_netvars(); +extern bool find_input_global(); +extern void init_x11(); +extern void init_conmsg(); +extern void install_menu(); + +static bool is_hl2_process() { + char buf[512]; + ssize_t n = readlink("/proc/self/exe", buf, sizeof(buf) - 1); + if (n <= 0) + return false; + buf[n] = 0; + for (ssize_t i = n - 1; i >= 0; i--) + if (buf[i] == '/') + return strcmp(buf + i + 1, "hl2_linux") == 0; + return false; +} + +static void *setup_thread(void *) { + for (int i = 0; i < 6000; i++) { + find_modules(); + if (g_client.valid() && g_engine.valid()) + break; + usleep(200 * 1000); + } + if (!g_client.valid() || !g_engine.valid()) { + logf("client.so/engine.so never appeared"); + return nullptr; + } + logf("client.so: %d mapped range(s), %s", (int)g_client.ranges.size(), + g_client.path.c_str()); + logf("engine.so: %d mapped range(s), %s", (int)g_engine.ranges.size(), + g_engine.path.c_str()); + + // resolve factories via dlopen handles + void *ch = dlopen(g_client.path.c_str(), RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); + void *eh = dlopen(g_engine.path.c_str(), RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL); + auto cf = (CreateInterfaceFn)dlsym(ch, "CreateInterface"); + auto ef = (CreateInterfaceFn)dlsym(eh, "CreateInterface"); + if (!cf || !ef) { + logf("CreateInterface not found"); + return nullptr; + } + + for (int i = 0; i < 300; i++) { + if (!g_iface.client) + g_iface.client = cf("VClient017", nullptr); + if (!g_iface.entitylist) + g_iface.entitylist = cf("VClientEntityList003", nullptr); + if (!g_iface.engine) + g_iface.engine = ef("VEngineClient014", nullptr); + if (g_iface.client && g_iface.entitylist && g_iface.engine) + break; + usleep(100 * 1000); + } + if (!g_iface.client || !g_iface.entitylist || !g_iface.engine) { + logf("interfaces unresolved: VClient017=%p VClientEntityList003=%p " + "VEngineClient014=%p", + g_iface.client, g_iface.entitylist, g_iface.engine); + return nullptr; + } + logf("interfaces: VClient017=%p VClientEntityList003=%p " + "VEngineClient014=%p", + g_iface.client, g_iface.entitylist, g_iface.engine); + + resolve_netvars(); + if (!find_input_global()) + return nullptr; + + init_conmsg(); + install_hook(); + install_menu(); + return nullptr; +} + +// --------------------------------------------------------------------------- +// constructor +// --------------------------------------------------------------------------- + +__attribute__((constructor)) static void hl2hake_load() { + if (!is_hl2_process()) + return; + logf("loaded (pid %d)", getpid()); + pthread_t t; + if (pthread_create(&t, nullptr, setup_thread, nullptr) == 0) + pthread_detach(t); + else + logf("pthread_create failed"); +} -- cgit v1.2.3