summaryrefslogtreecommitdiffstats
path: root/src/hl2h.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/hl2h.cpp')
-rw-r--r--src/hl2h.cpp307
1 files changed, 307 insertions, 0 deletions
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 @@
1// hl2h.cpp — core: logging, vector math, globals, feature logic, setup
2#include "hl2h.h"
3#include <cmath>
4#include <cstdarg>
5
6// ---------------------------------------------------------------------------
7// logging
8// ---------------------------------------------------------------------------
9
10void logf(const char *fmt, ...) {
11 va_list ap, ap2;
12 va_start(ap, fmt);
13 va_copy(ap2, ap);
14 fprintf(stderr, "hl2hake: ");
15 vfprintf(stderr, fmt, ap2);
16 fprintf(stderr, "\n");
17 fflush(stderr);
18 va_end(ap2);
19 if (FILE *f = fopen("/tmp/hl2hake.log", "a")) {
20 fprintf(f, "hl2hake: ");
21 vfprintf(f, fmt, ap);
22 fprintf(f, "\n");
23 fclose(f);
24 }
25 va_end(ap);
26}
27
28// ---------------------------------------------------------------------------
29// globals
30// ---------------------------------------------------------------------------
31
32Module g_client, g_engine;
33GameInterfaces g_iface;
34NetVars g_net;
35uintptr_t g_input = 0;
36
37// ---------------------------------------------------------------------------
38// vector math
39// ---------------------------------------------------------------------------
40
41static constexpr float PI_F = 3.14159265358979323846f;
42
43QAngle vector_angles(const Vector &forward) {
44 QAngle a{};
45 a.z = 0;
46 if (forward.y == 0 && forward.x == 0) {
47 a.y = 0;
48 a.x = (forward.z > 0) ? -90.f : 90.f;
49 } else {
50 a.y = atan2f(forward.y, forward.x) * 180.f / PI_F;
51 if (a.y < 0)
52 a.y += 360.f;
53 a.x = atan2f(-forward.z,
54 sqrtf(forward.x * forward.x + forward.y * forward.y)) *
55 180.f / PI_F;
56 if (a.x < 0)
57 a.x += 360.f;
58 }
59 return a;
60}
61
62Vector angle_forward_vector(const QAngle &angles) {
63 auto DEG2RAD = [](float x) { return x * (PI_F / 180.f); };
64 float sp = sinf(DEG2RAD(angles.x)), cp = cosf(DEG2RAD(angles.x));
65 float sy = sinf(DEG2RAD(angles.y)), cy = cosf(DEG2RAD(angles.y));
66 return {cp * cy, cp * sy, -sp};
67}
68
69float getfov(const QAngle &my_view, const QAngle &aim_ang) {
70 Vector aim = angle_forward_vector(my_view);
71 Vector ang = angle_forward_vector(aim_ang);
72 float len = sqrtf(aim.x * aim.x + aim.y * aim.y + aim.z * aim.z);
73 if (len <= 0)
74 return 0;
75 float dot = (aim.x * ang.x + aim.y * ang.y + aim.z * ang.z) / len;
76 if (dot > 1)
77 dot = 1;
78 if (dot < -1)
79 dot = -1;
80 return acosf(dot) * (180.f / PI_F);
81}
82
83// ---------------------------------------------------------------------------
84// feature logic (bhop, ABH, climb)
85// ---------------------------------------------------------------------------
86
87static int g_modified_logged = 0;
88
89void note_modified(const CUserCmd *cmd, int flags, const float *vel) {
90 // recompute usercmd CRC after modification
91 fix_verified_crc(cmd->command_number, usercmd_checksum(cmd));
92
93 if (g_modified_logged < 5) {
94 logf("cmd modified: seq=%d flags=0x%X buttons=0x%X fw=%.2f "
95 "vel=(%.1f,%.1f,%.1f)",
96 cmd->command_number, flags, cmd->buttons, cmd->forwardmove, vel[0],
97 vel[1], vel[2]);
98 g_modified_logged++;
99 }
100}
101
102// menu state (defined in menu.cpp, referenced here)
103
104static bool g_logged_local = false;
105
106static int engine_get_local_player() {
107 using Fn = int (*)(void *);
108 return vfunc<Fn>(g_iface.engine, 12)(g_iface.engine);
109}
110
111static void *entitylist_get(int idx) {
112 using Fn = void *(*)(void *, int);
113 return vfunc<Fn>(g_iface.entitylist, 3)(g_iface.entitylist, idx);
114}
115
116
117
118
119static void do_move(int seq) {
120 if (!g_input || !g_net.ok())
121 return;
122
123 int localIdx = engine_get_local_player();
124 if (localIdx <= 0 || localIdx > 64)
125 return;
126 if (!g_logged_local) {
127 logf("local player index %d (in-game)", localIdx);
128 g_logged_local = true;
129 }
130
131 void *ent = entitylist_get(localIdx);
132 if (!ent)
133 return;
134
135 CUserCmd *cmd = get_usercmd(seq);
136 if (!cmd)
137 return;
138
139 uintptr_t e = (uintptr_t)ent;
140 uint8_t movetype = *(uint8_t *)(e + g_net.movetype);
141 int flags = *(int *)(e + g_net.flags);
142 bool onground = (flags & FL_ONGROUND) != 0;
143 bool inwater = (flags & FL_INWATER) != 0;
144
145 if (movetype == MOVETYPE_NOCLIP || movetype == MOVETYPE_LADDER)
146 return;
147
148 static bool lastground = false;
149
150 if (middle_mouse_down()) {
151 if (cmd->command_number % 2) {
152 cmd->buttons &= ~IN_JUMP;
153 cmd->buttons |= IN_USE;
154 } else {
155 cmd->buttons &= ~IN_USE;
156 cmd->buttons |= IN_JUMP;
157 }
158 note_modified(cmd, flags, (float *)(e + g_net.velocity));
159 } else if (cmd->buttons & IN_JUMP) {
160 if (inwater && (cmd->buttons & IN_DUCK)) {
161 lastground = onground;
162 return;
163 }
164 const float *vel = (const float *)(e + g_net.velocity);
165 float speed = sqrtf(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]);
166 bool moving = speed > 0.1f;
167 bool backmove = false;
168 if (moving) {
169 QAngle moveang = vector_angles(Vector{vel[0], vel[1], vel[2]});
170 moveang.x = 0.f;
171 QAngle viewang{cmd->viewangles[0], cmd->viewangles[1],
172 cmd->viewangles[2]};
173 viewang.x = 0.f;
174 backmove = getfov(viewang, moveang) > 90.f;
175 }
176 if (!onground) {
177 lastground = false;
178 cmd->buttons &= ~IN_JUMP;
179 note_modified(cmd, flags, vel);
180 } else if (!lastground) {
181 lastground = true;
182 if (!backmove)
183 cmd->forwardmove = -0.1f;
184 note_modified(cmd, flags, vel);
185 }
186 }
187}
188
189// ---------------------------------------------------------------------------
190// CreateMove hook (cdecl — game virtuals use cdecl, not __thiscall)
191// ---------------------------------------------------------------------------
192
193using CreateMoveFn = bool (*)(void *, int, float, bool);
194static CreateMoveFn oCreateMove = nullptr;
195
196static bool hkdCreateMove(void *self, int seq, float frametime, bool active) {
197 bool ret = oCreateMove(self, seq, frametime, active);
198 do_move(seq);
199 return ret;
200}
201
202static void install_hook() {
203 void **vt = VTABLE(g_iface.client);
204 constexpr size_t SLOTS = 256;
205 void **newvt = new void *[SLOTS];
206 memcpy(newvt, vt, SLOTS * sizeof(void *));
207 oCreateMove = (CreateMoveFn)vt[21];
208 newvt[21] = (void *)&hkdCreateMove;
209 VTABLE(g_iface.client) = newvt;
210 logf("hook installed: CreateMove %p -> %p (orig %p)", vt[21],
211 (void *)&hkdCreateMove, (void *)oCreateMove);
212}
213
214// ---------------------------------------------------------------------------
215// setup thread
216// ---------------------------------------------------------------------------
217
218extern void find_modules();
219extern void resolve_netvars();
220extern bool find_input_global();
221extern void init_x11();
222extern void init_conmsg();
223extern void install_menu();
224
225static bool is_hl2_process() {
226 char buf[512];
227 ssize_t n = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
228 if (n <= 0)
229 return false;
230 buf[n] = 0;
231 for (ssize_t i = n - 1; i >= 0; i--)
232 if (buf[i] == '/')
233 return strcmp(buf + i + 1, "hl2_linux") == 0;
234 return false;
235}
236
237static void *setup_thread(void *) {
238 for (int i = 0; i < 6000; i++) {
239 find_modules();
240 if (g_client.valid() && g_engine.valid())
241 break;
242 usleep(200 * 1000);
243 }
244 if (!g_client.valid() || !g_engine.valid()) {
245 logf("client.so/engine.so never appeared");
246 return nullptr;
247 }
248 logf("client.so: %d mapped range(s), %s", (int)g_client.ranges.size(),
249 g_client.path.c_str());
250 logf("engine.so: %d mapped range(s), %s", (int)g_engine.ranges.size(),
251 g_engine.path.c_str());
252
253 // resolve factories via dlopen handles
254 void *ch = dlopen(g_client.path.c_str(), RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL);
255 void *eh = dlopen(g_engine.path.c_str(), RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL);
256 auto cf = (CreateInterfaceFn)dlsym(ch, "CreateInterface");
257 auto ef = (CreateInterfaceFn)dlsym(eh, "CreateInterface");
258 if (!cf || !ef) {
259 logf("CreateInterface not found");
260 return nullptr;
261 }
262
263 for (int i = 0; i < 300; i++) {
264 if (!g_iface.client)
265 g_iface.client = cf("VClient017", nullptr);
266 if (!g_iface.entitylist)
267 g_iface.entitylist = cf("VClientEntityList003", nullptr);
268 if (!g_iface.engine)
269 g_iface.engine = ef("VEngineClient014", nullptr);
270 if (g_iface.client && g_iface.entitylist && g_iface.engine)
271 break;
272 usleep(100 * 1000);
273 }
274 if (!g_iface.client || !g_iface.entitylist || !g_iface.engine) {
275 logf("interfaces unresolved: VClient017=%p VClientEntityList003=%p "
276 "VEngineClient014=%p",
277 g_iface.client, g_iface.entitylist, g_iface.engine);
278 return nullptr;
279 }
280 logf("interfaces: VClient017=%p VClientEntityList003=%p "
281 "VEngineClient014=%p",
282 g_iface.client, g_iface.entitylist, g_iface.engine);
283
284 resolve_netvars();
285 if (!find_input_global())
286 return nullptr;
287
288 init_conmsg();
289 install_hook();
290 install_menu();
291 return nullptr;
292}
293
294// ---------------------------------------------------------------------------
295// constructor
296// ---------------------------------------------------------------------------
297
298__attribute__((constructor)) static void hl2hake_load() {
299 if (!is_hl2_process())
300 return;
301 logf("loaded (pid %d)", getpid());
302 pthread_t t;
303 if (pthread_create(&t, nullptr, setup_thread, nullptr) == 0)
304 pthread_detach(t);
305 else
306 logf("pthread_create failed");
307}