summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-28 12:24:10 +0100
committersalmonik <salmonik@tuta.io>2026-10-28 12:24:10 +0100
commit16546e5818408f35adc60c2d937cf4541ff7840a (patch)
tree19c405c2c226e69f082d9465f9325c67bb1d0d92
parentItem ESP: recover CPhysicsProp model names from the server (diff)
downloadhl2h-16546e5818408f35adc60c2d937cf4541ff7840a.tar.gz
hl2h-16546e5818408f35adc60c2d937cf4541ff7840a.zip
Logging: per-session file and debug-gated verbose logs
- Open /tmp/hl2hake.log once in the library constructor with "w" so each session starts clean, use line buffering, flush each line, and close it via atexit. No more fopen/fclose per message. - Add logd() for verbose diagnostics, enabled only with HL2H_DEBUG=1. - Move ESP/NPC scans, item-prop/item-classification logs, aimbot/silent-aim/ melee/triggerbot/spread details, jumpbug tracking, bone dumps, netvar dumps, cmd-modified and ground-trace noise to logd(). - Keep logf() for lifecycle, config, warnings/errors, GUI open/close, item category/tagging summaries and item-crate detections so crate debugging works without the ESP stream. - Supply crates now respect the max-distance slider (user change), with the stale comment and CONTEXT updated. - Document HL2H_DEBUG=1 in play.sh and CONTEXT.
Diffstat (limited to '')
-rw-r--r--CONTEXT.md14
-rwxr-xr-xplay.sh1
-rw-r--r--src/bones.cpp10
-rw-r--r--src/cinput.cpp2
-rw-r--r--src/esp.cpp52
-rw-r--r--src/hl2h.cpp76
-rw-r--r--src/hl2h.h1
-rw-r--r--src/menu.cpp4
-rw-r--r--src/netvars.cpp14
-rw-r--r--src/server.cpp2
-rw-r--r--src/vischeck.cpp2
11 files changed, 111 insertions, 67 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index 737a6b2..7a1ee33 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -258,6 +258,15 @@ vocabulary / decisions.
258 assist deliberately writes only `m_vecAbsVelocity`; `SetAbsVelocity` syncs 258 assist deliberately writes only `m_vecAbsVelocity`; `SetAbsVelocity` syncs
259 `m_vecVelocity` after the command. 259 `m_vecVelocity` after the command.
260 260
261## Logging
262
263- `/tmp/hl2hake.log` is opened once at startup with `"w"` (so it only contains
264 the current session), line-buffered, and closed via `atexit`.
265- `logf` is always on: lifecycle, warnings/errors, config, GUI open/close and
266 item-crate detections.
267- `logd` is off unless `HL2H_DEBUG=1`; it carries per-scan ESP/NPC/item-prop,
268 aimbot-target, bone and netvar noise. Run `HL2H_DEBUG=1 ./play.sh` to enable.
269
261## Gotchas / lessons 270## Gotchas / lessons
262 271
263- Never pipe-mask build failures: 272- Never pipe-mask build failures:
@@ -275,9 +284,8 @@ vocabulary / decisions.
275- Scans the client entity list every 100 ms and tags health, ammo, armor/suit, 284- Scans the client entity list every 100 ms and tags health, ammo, armor/suit,
276 world weapon pickups, the two supply-crate models and generic items. 285 world weapon pickups, the two supply-crate models and generic items.
277- Each tag is a colored marker with a name and optional distance, filtered by 286- Each tag is a colored marker with a name and optional distance, filtered by
278 category toggles and a max-distance slider (default 5000 units). Supply 287 category toggles and a max-distance slider (default 5000 units). Drawn
279 crates ignore the distance limit. Drawn through walls and independently of 288 through walls and independently of the hostile NPC ESP.
280 the hostile NPC ESP.
281- Ammo labels are specific where the model is known: pistol, SMG, AR2, .357, 289- Ammo labels are specific where the model is known: pistol, SMG, AR2, .357,
282 crossbow, shotgun, sniper, flares, AR2 grenade, RPG rocket, plus ammo-crate 290 crossbow, shotgun, sniper, flares, AR2 grenade, RPG rocket, plus ammo-crate
283 variants. Unknown ammo falls back to "Ammo". 291 variants. Unknown ammo falls back to "Ammo".
diff --git a/play.sh b/play.sh
index 4e52b6c..ff756aa 100755
--- a/play.sh
+++ b/play.sh
@@ -1,6 +1,7 @@
1#!/bin/sh 1#!/bin/sh
2# Launch HL2 with hl2hake preloaded (through Steam's bundled 32-bit runtime). 2# Launch HL2 with hl2hake preloaded (through Steam's bundled 32-bit runtime).
3# Extra args are passed to the game, e.g.: ./play.sh -windowed +map d1_trainstation_01 3# Extra args are passed to the game, e.g.: ./play.sh -windowed +map d1_trainstation_01
4# Set HL2H_DEBUG=1 to include verbose diagnostic logging.
4set -e 5set -e
5GAME="$HOME/.local/share/Steam/steamapps/common/Half-Life 2" 6GAME="$HOME/.local/share/Steam/steamapps/common/Half-Life 2"
6LIB="$(cd "$(dirname "$0")" && pwd)/libhl2hake.so" 7LIB="$(cd "$(dirname "$0")" && pwd)/libhl2hake.so"
diff --git a/src/bones.cpp b/src/bones.cpp
index 787af1b..0df13f2 100644
--- a/src/bones.cpp
+++ b/src/bones.cpp
@@ -265,22 +265,22 @@ static void build_core_mask(BoneEntry *e) {
265 265
266static void dump_hitboxes(const BoneEntry *e) { 266static void dump_hitboxes(const BoneEntry *e) {
267 const StudioHdr *h = e->hdr; 267 const StudioHdr *h = e->hdr;
268 logf("hitbox dump: idx=%d sets=%d setidx=%d", e->idx, h->numhitboxsets, 268 logd("hitbox dump: idx=%d sets=%d setidx=%d", e->idx, h->numhitboxsets,
269 h->hitboxsetindex); 269 h->hitboxsetindex);
270 const HitboxSet *s = hitbox_set0(h); 270 const HitboxSet *s = hitbox_set0(h);
271 if (!s) { 271 if (!s) {
272 logf("hitbox dump: no usable hitbox set"); 272 logd("hitbox dump: no usable hitbox set");
273 return; 273 return;
274 } 274 }
275 const Hitbox *hb = hitboxes(s); 275 const Hitbox *hb = hitboxes(s);
276 logf("hitbox dump: set0 n=%d hitboxindex=%d", s->numhitboxes, 276 logd("hitbox dump: set0 n=%d hitboxindex=%d", s->numhitboxes,
277 s->hitboxindex); 277 s->hitboxindex);
278 int n = s->numhitboxes < 24 ? s->numhitboxes : 24; 278 int n = s->numhitboxes < 24 ? s->numhitboxes : 24;
279 for (int i = 0; i < n; i++) { 279 for (int i = 0; i < n; i++) {
280 const char *bn = (hb[i].bone >= 0 && hb[i].bone < e->count) 280 const char *bn = (hb[i].bone >= 0 && hb[i].bone < e->count)
281 ? bone_name_raw(h, hb[i].bone) 281 ? bone_name_raw(h, hb[i].bone)
282 : nullptr; 282 : nullptr;
283 logf(" hb %d name='%s' bone=%d(%s) bb=(%.1f %.1f %.1f)-(%.1f %.1f %.1f)", 283 logd(" hb %d name='%s' bone=%d(%s) bb=(%.1f %.1f %.1f)-(%.1f %.1f %.1f)",
284 i, hitbox_name(hb[i]), hb[i].bone, bn ? bn : "?", hb[i].bbmin.x, 284 i, hitbox_name(hb[i]), hb[i].bone, bn ? bn : "?", hb[i].bbmin.x,
285 hb[i].bbmin.y, hb[i].bbmin.z, hb[i].bbmax.x, hb[i].bbmax.y, 285 hb[i].bbmin.y, hb[i].bbmin.z, hb[i].bbmax.x, hb[i].bbmax.y,
286 hb[i].bbmax.z); 286 hb[i].bbmax.z);
@@ -370,7 +370,7 @@ bool bones_update(int idx, void *ent) {
370 e->stamp = now; 370 e->stamp = now;
371 if (g_logged < 4) { 371 if (g_logged < 4) {
372 g_logged++; 372 g_logged++;
373 logf("bones: idx=%d count=%d invalid=%d bone0=%s", idx, e->count, 373 logd("bones: idx=%d count=%d invalid=%d bone0=%s", idx, e->count,
374 invalid_bones, 374 invalid_bones,
375 bone_name_raw(hdr, 0) ? bone_name_raw(hdr, 0) : "?"); 375 bone_name_raw(hdr, 0) ? bone_name_raw(hdr, 0) : "?");
376 } 376 }
diff --git a/src/cinput.cpp b/src/cinput.cpp
index f466c53..08686a1 100644
--- a/src/cinput.cpp
+++ b/src/cinput.cpp
@@ -225,7 +225,7 @@ bool find_input_global() {
225 uintptr_t f3 = *(uintptr_t *)(vtbl + 3 * sizeof(void *)); 225 uintptr_t f3 = *(uintptr_t *)(vtbl + 3 * sizeof(void *));
226 if (!g_client.contains(f3)) 226 if (!g_client.contains(f3))
227 continue; 227 continue;
228 logf("CInput* = %#lx (ptr var %#lx, vfunc[3] %#lx)", (unsigned long)input, 228 logd("CInput* = %#lx (ptr var %#lx, vfunc[3] %#lx)", (unsigned long)input,
229 (unsigned long)var, (unsigned long)f3); 229 (unsigned long)var, (unsigned long)f3);
230 g_input = input; 230 g_input = input;
231 if (!install_input_hook()) 231 if (!install_input_hook())
diff --git a/src/esp.cpp b/src/esp.cpp
index ec3e94b..50549a1 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -208,7 +208,7 @@ static void esp_collect(int local_idx) {
208 g_net.modelindex >= 0 ? *(int *)(e + g_net.modelindex) : -1; 208 g_net.modelindex >= 0 ? *(int *)(e + g_net.modelindex) : -1;
209 int srv_mi = server_model_index(sbase); 209 int srv_mi = server_model_index(sbase);
210 srv_trusted = (cli_mi == srv_mi); 210 srv_trusted = (cli_mi == srv_mi);
211 logf("ESP server check: idx=%d base=%p cli_model=%d srv_model=%d " 211 logd("ESP server check: idx=%d base=%p cli_model=%d srv_model=%d "
212 "hp=%d maxhp=%d life=%d trusted=%d", 212 "hp=%d maxhp=%d life=%d trusted=%d",
213 idx, sbase, cli_mi, srv_mi, shp, smax, slife, (int)srv_trusted); 213 idx, sbase, cli_mi, srv_mi, shp, smax, slife, (int)srv_trusted);
214 if (!srv_trusted) 214 if (!srv_trusted)
@@ -223,7 +223,7 @@ static void esp_collect(int local_idx) {
223 223
224 if (dbg && npc && dbg_lines < 40) { 224 if (dbg && npc && dbg_lines < 40) {
225 dbg_lines++; 225 dbg_lines++;
226 logf("ESP npc %d cls=%s life=%d(cli %d) hp=%d(cli %d) maxhp=%d model=%s", 226 logd("ESP npc %d cls=%s life=%d(cli %d) hp=%d(cli %d) maxhp=%d model=%s",
227 idx, cls, life, cli_life, hp, cli_hp, maxhp, 227 idx, cls, life, cli_life, hp, cli_hp, maxhp,
228 model ? model : "?"); 228 model ? model : "?");
229 } 229 }
@@ -266,10 +266,10 @@ static void esp_collect(int local_idx) {
266 } 266 }
267 267
268 if (dbg) 268 if (dbg)
269 logf("ESP scan: hi=%d npc_alive=%d targets=%d", hi, npc_alive, g_ntargets); 269 logd("ESP scan: hi=%d npc_alive=%d targets=%d", hi, npc_alive, g_ntargets);
270 if (g_ntargets && !g_esp_logged) { 270 if (g_ntargets && !g_esp_logged) {
271 g_esp_logged = true; 271 g_esp_logged = true;
272 logf("ESP: first targets, %d", g_ntargets); 272 logd("ESP: first targets, %d", g_ntargets);
273 } 273 }
274} 274}
275 275
@@ -442,7 +442,7 @@ static void draw_skeleton(const EspTarget &t) {
442 static int logged = 0; 442 static int logged = 0;
443 if (core_count > 0 && logged < 4) { 443 if (core_count > 0 && logged < 4) {
444 logged++; 444 logged++;
445 logf("skeleton: idx=%d bones=%d core=%d humanoid=%d", t.idx, n, core_count, 445 logd("skeleton: idx=%d bones=%d core=%d humanoid=%d", t.idx, n, core_count,
446 (int)bones_is_humanoid(t.idx)); 446 (int)bones_is_humanoid(t.idx));
447 } 447 }
448 if (t.friendly) 448 if (t.friendly)
@@ -565,7 +565,7 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
565 static int lock_logs = 0; 565 static int lock_logs = 0;
566 if (lock_logs < 5) { 566 if (lock_logs < 5) {
567 lock_logs++; 567 lock_logs++;
568 logf("%s: lock idx=%d fov=%.2f", tag, out_idx, f); 568 logd("%s: lock idx=%d fov=%.2f", tag, out_idx, f);
569 } 569 }
570 return true; 570 return true;
571 } 571 }
@@ -636,7 +636,7 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
636 double now = esp_now(); 636 double now = esp_now();
637 if (now - last_none >= 2.0) { 637 if (now - last_none >= 2.0) {
638 last_none = now; 638 last_none = now;
639 logf("%s: no hostile target within %.0f deg", tag, max_fov); 639 logd("%s: no hostile target within %.0f deg", tag, max_fov);
640 } 640 }
641 return false; 641 return false;
642 } 642 }
@@ -654,7 +654,7 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
654 static int sel_logs = 0; 654 static int sel_logs = 0;
655 if (sel_logs < 5) { 655 if (sel_logs < 5) {
656 sel_logs++; 656 sel_logs++;
657 logf("%s: target idx=%d fov=%.2f vischeck=%d", tag, out_idx, f, 657 logd("%s: target idx=%d fov=%.2f vischeck=%d", tag, out_idx, f,
658 (int)vischeck); 658 (int)vischeck);
659 } 659 }
660 return true; 660 return true;
@@ -664,7 +664,7 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
664 double now = esp_now(); 664 double now = esp_now();
665 if (now - last_vis >= 2.0) { 665 if (now - last_vis >= 2.0) {
666 last_vis = now; 666 last_vis = now;
667 logf("%s: no visible aim point (vischeck=%d)", tag, (int)vischeck); 667 logd("%s: no visible aim point (vischeck=%d)", tag, (int)vischeck);
668 } 668 }
669 return false; 669 return false;
670} 670}
@@ -688,7 +688,7 @@ bool aimbot_apply(CUserCmd *cmd) {
688 static int want_logs = 0; 688 static int want_logs = 0;
689 if (want_logs < 10) { 689 if (want_logs < 10) {
690 want_logs++; 690 want_logs++;
691 logf("aimbot: target=%d aim=(%.1f %.1f %.1f) fov=%.2f want=%.2f/%.2f", 691 logd("aimbot: target=%d aim=(%.1f %.1f %.1f) fov=%.2f want=%.2f/%.2f",
692 target, point.x, point.y, point.z, fov, desired.x, desired.y); 692 target, point.x, point.y, point.z, fov, desired.x, desired.y);
693 } 693 }
694 float dp = desired.x - cmd->viewangles[0]; 694 float dp = desired.x - cmd->viewangles[0];
@@ -723,7 +723,7 @@ bool aimbot_apply(CUserCmd *cmd) {
723 static int mod_logs = 0; 723 static int mod_logs = 0;
724 if (mod_logs < 10) { 724 if (mod_logs < 10) {
725 mod_logs++; 725 mod_logs++;
726 logf("aimbot: view %.2f/%.2f -> %.2f/%.2f", old_pitch, old_yaw, np, ny); 726 logd("aimbot: view %.2f/%.2f -> %.2f/%.2f", old_pitch, old_yaw, np, ny);
727 } 727 }
728 return true; 728 return true;
729} 729}
@@ -785,7 +785,7 @@ bool silent_aim_apply(CUserCmd *cmd) {
785 static int logs = 0; 785 static int logs = 0;
786 if (logs < 5) { 786 if (logs < 5) {
787 logs++; 787 logs++;
788 logf("silentaim: fire idx=%d fov=%.2f aim=%.2f/%.2f", target, fov, np, ny); 788 logd("silentaim: fire idx=%d fov=%.2f aim=%.2f/%.2f", target, fov, np, ny);
789 } 789 }
790 return true; 790 return true;
791} 791}
@@ -855,7 +855,7 @@ bool melee_weapon_active(void *local_ent) {
855 if (idx != last_idx || crowbar != last_crowbar) { 855 if (idx != last_idx || crowbar != last_crowbar) {
856 last_idx = idx; 856 last_idx = idx;
857 last_crowbar = crowbar; 857 last_crowbar = crowbar;
858 logf("melee: active idx=%d cls=%s model=%s crowbar=%d", idx, 858 logd("melee: active idx=%d cls=%s model=%s crowbar=%d", idx,
859 cls ? cls : "?", model ? model : "?", (int)crowbar); 859 cls ? cls : "?", model ? model : "?", (int)crowbar);
860 } 860 }
861 return crowbar; 861 return crowbar;
@@ -1033,7 +1033,7 @@ bool melee_aimbot_apply(CUserCmd *cmd) {
1033 static int logs = 0; 1033 static int logs = 0;
1034 if (logs < 10) { 1034 if (logs < 10) {
1035 logs++; 1035 logs++;
1036 logf("melee: swing idx=%d dist=%.1f aim=(%.1f %.1f %.1f)", c.idx, 1036 logd("melee: swing idx=%d dist=%.1f aim=(%.1f %.1f %.1f)", c.idx,
1037 c.dist, aim.x, aim.y, aim.z); 1037 c.dist, aim.x, aim.y, aim.z);
1038 } 1038 }
1039 return true; 1039 return true;
@@ -1043,7 +1043,7 @@ bool melee_aimbot_apply(CUserCmd *cmd) {
1043 double now = esp_now(); 1043 double now = esp_now();
1044 if (now - last_none >= 2.0) { 1044 if (now - last_none >= 2.0) {
1045 last_none = now; 1045 last_none = now;
1046 logf("melee: no visible target within %.0f", MELEE_RANGE); 1046 logd("melee: no visible target within %.0f", MELEE_RANGE);
1047 } 1047 }
1048 return false; 1048 return false;
1049} 1049}
@@ -1132,7 +1132,7 @@ static void spread_compute() {
1132 g_spread_seed12 = window(12, 0); 1132 g_spread_seed12 = window(12, 0);
1133 seed(0); 1133 seed(0);
1134 1134
1135 logf("spread: seeds single=%d shotgun8=%d shotgun12=%d", g_spread_seed1, 1135 logd("spread: seeds single=%d shotgun8=%d shotgun12=%d", g_spread_seed1,
1136 g_spread_seed8, g_spread_seed12); 1136 g_spread_seed8, g_spread_seed12);
1137 g_spread_ready = true; 1137 g_spread_ready = true;
1138} 1138}
@@ -1182,7 +1182,7 @@ bool spread_control_apply(CUserCmd *cmd, void *local_ent) {
1182 static int logs = 0; 1182 static int logs = 0;
1183 if (logs < 10) { 1183 if (logs < 10) {
1184 logs++; 1184 logs++;
1185 logf("spread: seed=%d pellets=%d", seed, pellets); 1185 logd("spread: seed=%d pellets=%d", seed, pellets);
1186 } 1186 }
1187 return true; 1187 return true;
1188} 1188}
@@ -1243,7 +1243,7 @@ bool triggerbot_apply(CUserCmd *cmd) {
1243 static int logs = 0; 1243 static int logs = 0;
1244 if (logs < 5) { 1244 if (logs < 5) {
1245 logs++; 1245 logs++;
1246 logf("triggerbot: fire idx=%d model=%s", idx, model ? model : "?"); 1246 logd("triggerbot: fire idx=%d model=%s", idx, model ? model : "?");
1247 } 1247 }
1248 return true; 1248 return true;
1249 } 1249 }
@@ -1255,7 +1255,7 @@ void esp_draw() {
1255 if (!g_scr_w) { 1255 if (!g_scr_w) {
1256 using Fn = void (*)(void *, int &, int &); 1256 using Fn = void (*)(void *, int &, int &);
1257 vfunc<Fn>(g_surface, SURF_GetScreenSize)(g_surface, g_scr_w, g_scr_h); 1257 vfunc<Fn>(g_surface, SURF_GetScreenSize)(g_surface, g_scr_w, g_scr_h);
1258 logf("ESP: screen %dx%d", g_scr_w, g_scr_h); 1258 logd("ESP: screen %dx%d", g_scr_w, g_scr_h);
1259 if (!g_scr_w) return; 1259 if (!g_scr_w) return;
1260 } 1260 }
1261 draw_fov_circle(); 1261 draw_fov_circle();
@@ -1323,7 +1323,7 @@ void esp_draw() {
1323 static int no_head = 0; 1323 static int no_head = 0;
1324 if (no_head < 4) { 1324 if (no_head < 4) {
1325 no_head++; 1325 no_head++;
1326 logf("head: idx=%d no head hitbox/bone", t.idx); 1326 logd("head: idx=%d no head hitbox/bone", t.idx);
1327 } 1327 }
1328 continue; 1328 continue;
1329 } 1329 }
@@ -1334,7 +1334,7 @@ void esp_draw() {
1334 surf_rect(1, (int)hx - 2, (int)hy - 2, (int)hx + 3, (int)hy + 3); 1334 surf_rect(1, (int)hx - 2, (int)hy - 2, (int)hx + 3, (int)hy + 3);
1335 if (!head_logged) { 1335 if (!head_logged) {
1336 head_logged = true; 1336 head_logged = true;
1337 logf("bone debug: idx=%d bones=%d head=(%.1f %.1f %.1f)", t.idx, 1337 logd("bone debug: idx=%d bones=%d head=(%.1f %.1f %.1f)", t.idx,
1338 bones_count(t.idx), head.x, head.y, head.z); 1338 bones_count(t.idx), head.x, head.y, head.z);
1339 } 1339 }
1340 } 1340 }
@@ -1700,7 +1700,7 @@ static void items_collect(int local_idx) {
1700 1700
1701 if (propish && dist < 1500.0f && prop_logs < 40) { 1701 if (propish && dist < 1500.0f && prop_logs < 40) {
1702 prop_logs++; 1702 prop_logs++;
1703 logf("item prop: idx=%d cls=%s model=%s kind=%d dist=%.0f " 1703 logd("item prop: idx=%d cls=%s model=%s kind=%d dist=%.0f "
1704 "origin=(%.0f %.0f %.0f)", 1704 "origin=(%.0f %.0f %.0f)",
1705 idx, cls ? cls : "?", model ? model : "?", kind, dist, origin.x, 1705 idx, cls ? cls : "?", model ? model : "?", kind, dist, origin.x,
1706 origin.y, origin.z); 1706 origin.y, origin.z);
@@ -1721,10 +1721,8 @@ static void items_collect(int local_idx) {
1721 if (idx == active_idx || item_weapon_is_owned(ent)) 1721 if (idx == active_idx || item_weapon_is_owned(ent))
1722 continue; 1722 continue;
1723 } 1723 }
1724 // Crates are rare and always worth knowing about; the distance slider 1724 // Distance applies to every tag, including supply crates.
1725 // filters the common ammo/health clutter. 1725 if (g_item_max_dist > 0 && dist > (float)g_item_max_dist)
1726 if (kind != IK_CRATE && g_item_max_dist > 0 &&
1727 dist > (float)g_item_max_dist)
1728 continue; 1726 continue;
1729 1727
1730 ItemTarget &t = g_item_targets[g_nitems++]; 1728 ItemTarget &t = g_item_targets[g_nitems++];
@@ -1734,7 +1732,7 @@ static void items_collect(int local_idx) {
1734 snprintf(t.label, sizeof(t.label), "%s", label); 1732 snprintf(t.label, sizeof(t.label), "%s", label);
1735 if (dbg_lines < 8) { 1733 if (dbg_lines < 8) {
1736 dbg_lines++; 1734 dbg_lines++;
1737 logf("item: idx=%d kind=%d cls=%s model=%s", idx, kind, 1735 logd("item: idx=%d kind=%d cls=%s model=%s", idx, kind,
1738 cls ? cls : "?", model ? model : "?"); 1736 cls ? cls : "?", model ? model : "?");
1739 } 1737 }
1740 } 1738 }
diff --git a/src/hl2h.cpp b/src/hl2h.cpp
index 8c6b5db..256b9ab 100644
--- a/src/hl2h.cpp
+++ b/src/hl2h.cpp
@@ -10,21 +10,56 @@
10// logging 10// logging
11// --------------------------------------------------------------------------- 11// ---------------------------------------------------------------------------
12 12
13void logf(const char *fmt, ...) { 13static FILE *g_log = nullptr;
14 va_list ap, ap2; 14static bool g_log_debug = false;
15 va_start(ap, fmt); 15static pthread_mutex_t g_log_mu = PTHREAD_MUTEX_INITIALIZER;
16 va_copy(ap2, ap); 16
17 fprintf(stderr, "hl2hake: "); 17static void log_close() {
18 vfprintf(stderr, fmt, ap2); 18 pthread_mutex_lock(&g_log_mu);
19 fprintf(stderr, "\n"); 19 if (g_log) {
20 fclose(g_log);
21 g_log = nullptr;
22 }
23 pthread_mutex_unlock(&g_log_mu);
24}
25
26// Open once per process and truncate, so /tmp/hl2hake.log only ever contains
27// the current session. HL2H_DEBUG=1 enables logd() noise.
28static void log_open() {
29 const char *dbg = getenv("HL2H_DEBUG");
30 g_log_debug = dbg && *dbg && strcmp(dbg, "0") != 0;
31 g_log = fopen("/tmp/hl2hake.log", "w");
32 if (g_log)
33 setvbuf(g_log, nullptr, _IOLBF, 0);
34 atexit(log_close);
35}
36
37static void log_write(const char *fmt, va_list ap) {
38 char buf[4096];
39 vsnprintf(buf, sizeof(buf), fmt, ap);
40 pthread_mutex_lock(&g_log_mu);
41 fprintf(stderr, "hl2hake: %s\n", buf);
20 fflush(stderr); 42 fflush(stderr);
21 va_end(ap2); 43 if (g_log) {
22 if (FILE *f = fopen("/tmp/hl2hake.log", "a")) { 44 fprintf(g_log, "hl2hake: %s\n", buf);
23 fprintf(f, "hl2hake: "); 45 fflush(g_log);
24 vfprintf(f, fmt, ap);
25 fprintf(f, "\n");
26 fclose(f);
27 } 46 }
47 pthread_mutex_unlock(&g_log_mu);
48}
49
50void logf(const char *fmt, ...) {
51 va_list ap;
52 va_start(ap, fmt);
53 log_write(fmt, ap);
54 va_end(ap);
55}
56
57void logd(const char *fmt, ...) {
58 if (!g_log_debug)
59 return;
60 va_list ap;
61 va_start(ap, fmt);
62 log_write(fmt, ap);
28 va_end(ap); 63 va_end(ap);
29} 64}
30 65
@@ -93,7 +128,7 @@ void note_modified(const CUserCmd *cmd, int flags, const float *vel) {
93 fix_verified_crc(cmd->command_number, usercmd_checksum(cmd)); 128 fix_verified_crc(cmd->command_number, usercmd_checksum(cmd));
94 129
95 if (g_modified_logged < 5) { 130 if (g_modified_logged < 5) {
96 logf("cmd modified: seq=%d flags=0x%X buttons=0x%X fw=%.2f " 131 logd("cmd modified: seq=%d flags=0x%X buttons=0x%X fw=%.2f "
97 "vel=(%.1f,%.1f,%.1f)", 132 "vel=(%.1f,%.1f,%.1f)",
98 cmd->command_number, flags, cmd->buttons, cmd->forwardmove, vel[0], 133 cmd->command_number, flags, cmd->buttons, cmd->forwardmove, vel[0],
99 vel[1], vel[2]); 134 vel[1], vel[2]);
@@ -311,7 +346,7 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags,
311 } 346 }
312 if (!jb_tracked) { 347 if (!jb_tracked) {
313 jb_tracked = true; 348 jb_tracked = true;
314 logf("jumpbug: tracking x=%.1f vz=%.1f impact=%.1f", x, vz, impact); 349 logd("jumpbug: tracking x=%.1f vz=%.1f impact=%.1f", x, vz, impact);
315 } 350 }
316 351
317 bool assist = jumpbug_assist_enabled(); 352 bool assist = jumpbug_assist_enabled();
@@ -345,7 +380,7 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags,
345 jb_tracked = false; 380 jb_tracked = false;
346 if (jb_logs < 30) { 381 if (jb_logs < 30) {
347 jb_logs++; 382 jb_logs++;
348 logf("jumpbug: trigger x=%.2f drop=%.2f vz=%.1f assist=%d", x, jb_drop, 383 logd("jumpbug: trigger x=%.2f drop=%.2f vz=%.1f assist=%d", x, jb_drop,
349 vz, (int)assist); 384 vz, (int)assist);
350 } 385 }
351 } else { 386 } else {
@@ -357,7 +392,7 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags,
357 if (jb_force_next_distance(local, ent, vel, x, JB_TARGET_DIST)) { 392 if (jb_force_next_distance(local, ent, vel, x, JB_TARGET_DIST)) {
358 if (jb_logs < 30) { 393 if (jb_logs < 30) {
359 jb_logs++; 394 jb_logs++;
360 logf("jumpbug: assist duck x=%.2f next=%.2f -> vz=%.1f", x, 395 logd("jumpbug: assist duck x=%.2f next=%.2f -> vz=%.1f", x,
361 next_x, vel[2]); 396 next_x, vel[2]);
362 } 397 }
363 } else { 398 } else {
@@ -391,10 +426,10 @@ static bool jumpbug_apply(CUserCmd *cmd, int local, void *ent, int flags,
391 if (jb_logs < 30) { 426 if (jb_logs < 30) {
392 jb_logs++; 427 jb_logs++;
393 if (assist) 428 if (assist)
394 logf("jumpbug: setup assist x=%.2f drop=%.2f vz=%.1f ok=%d", x, 429 logd("jumpbug: setup assist x=%.2f drop=%.2f vz=%.1f ok=%d", x,
395 jb_drop, vel[2], (int)forced); 430 jb_drop, vel[2], (int)forced);
396 else 431 else
397 logf("jumpbug: setup x=%.2f drop=%.2f pred=%.2f vz=%.1f", x, 432 logd("jumpbug: setup x=%.2f drop=%.2f pred=%.2f vz=%.1f", x,
398 jb_drop, x + JB_DUCK_RAISE - jb_drop, vz); 433 jb_drop, x + JB_DUCK_RAISE - jb_drop, vz);
399 } 434 }
400 } 435 }
@@ -458,7 +493,7 @@ static void hkdSrvRandomSeed(int seed) {
458 if (trace && (bullet_site || logs < 12)) { 493 if (trace && (bullet_site || logs < 12)) {
459 if (logs < 200) { 494 if (logs < 200) {
460 logs++; 495 logs++;
461 logf("srvseed: seed=%d off=0x%lx bullet=%d self=%p local=%d", seed, off, 496 logd("srvseed: seed=%d off=0x%lx bullet=%d self=%p local=%d", seed, off,
462 (int)bullet_site, self, (int)local); 497 (int)bullet_site, self, (int)local);
463 } 498 }
464 } 499 }
@@ -893,6 +928,7 @@ static void *setup_thread(void *) {
893__attribute__((constructor)) static void hl2hake_load() { 928__attribute__((constructor)) static void hl2hake_load() {
894 if (!is_hl2_process()) 929 if (!is_hl2_process())
895 return; 930 return;
931 log_open();
896 logf("loaded (pid %d) build %s %s", getpid(), __DATE__, __TIME__); 932 logf("loaded (pid %d) build %s %s", getpid(), __DATE__, __TIME__);
897 pthread_t t; 933 pthread_t t;
898 if (pthread_create(&t, nullptr, setup_thread, nullptr) == 0) 934 if (pthread_create(&t, nullptr, setup_thread, nullptr) == 0)
diff --git a/src/hl2h.h b/src/hl2h.h
index cc11e0d..9b4fa8a 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -21,6 +21,7 @@ constexpr float PI_F = 3.14159265f;
21// --------------------------------------------------------------------------- 21// ---------------------------------------------------------------------------
22 22
23void logf(const char *fmt, ...); 23void logf(const char *fmt, ...);
24void logd(const char *fmt, ...); // debug-only (HL2H_DEBUG=1)
24 25
25// --------------------------------------------------------------------------- 26// ---------------------------------------------------------------------------
26// on-screen toasts 27// on-screen toasts
diff --git a/src/menu.cpp b/src/menu.cpp
index 9d07b72..6d7143d 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -682,7 +682,7 @@ void outer_draw() {
682 } 682 }
683 if (font_reset && g_font) { 683 if (font_reset && g_font) {
684 g_font = 0; 684 g_font = 0;
685 logf("font: reset (local entity change)"); 685 logd("font: reset (local entity change)");
686 } 686 }
687 687
688 bool overlay_font = true; 688 bool overlay_font = true;
@@ -697,7 +697,7 @@ void outer_draw() {
697 continue; 697 continue;
698 if (surf_set_font_glyph_set(g_font, families[fi], 16, 400, 0, 0, 0x400, 698 if (surf_set_font_glyph_set(g_font, families[fi], 16, 400, 0, 0, 0x400,
699 0x20, 0xFF)) { 699 0x20, 0xFF)) {
700 logf("outer_draw: font family %s", families[fi]); 700 logd("outer_draw: font family %s", families[fi]);
701 font_ok = true; 701 font_ok = true;
702 } else { 702 } else {
703 g_font = 0; 703 g_font = 0;
diff --git a/src/netvars.cpp b/src/netvars.cpp
index 4980acc..adb5a6a 100644
--- a/src/netvars.cpp
+++ b/src/netvars.cpp
@@ -46,7 +46,7 @@ static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth) {
46 strcmp(tname, "DT_BasePlayer") == 0); 46 strcmp(tname, "DT_BasePlayer") == 0);
47 if (dump) { 47 if (dump) {
48 g_dumped_tables++; 48 g_dumped_tables++;
49 logf("dump %s: %d props @ table %#lx (base_off %#lx)", tname, nprops, 49 logd("dump %s: %d props @ table %#lx (base_off %#lx)", tname, nprops,
50 (unsigned long)table, (unsigned long)base_off); 50 (unsigned long)table, (unsigned long)base_off);
51 } 51 }
52 52
@@ -65,7 +65,7 @@ static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth) {
65 continue; 65 continue;
66 66
67 if (dump) 67 if (dump)
68 logf(" [%s] %s type=%d off=%d(0x%x) table=%s array=%s", tname, pname2, 68 logd(" [%s] %s type=%d off=%d(0x%x) table=%s array=%s", tname, pname2,
69 ptype, poff, poff, ptable ? "yes" : "no", parray ? "yes" : "no"); 69 ptype, poff, poff, ptable ? "yes" : "no", parray ? "yes" : "no");
70 if (ptable) { 70 if (ptable) {
71 collect_netvars(ptable, base_off + (uint32_t)poff, depth + 1); 71 collect_netvars(ptable, base_off + (uint32_t)poff, depth + 1);
@@ -150,7 +150,7 @@ static bool resolve_movetype() {
150 break; 150 break;
151 } 151 }
152 if (!str_va) { 152 if (!str_va) {
153 logf("m_MoveType string not found"); 153 logd("m_MoveType string not found");
154 return false; 154 return false;
155 } 155 }
156 156
@@ -181,7 +181,7 @@ static bool resolve_movetype() {
181 } 181 }
182 } 182 }
183 if (ncand == 0) { 183 if (ncand == 0) {
184 logf("m_MoveType typedescription not found"); 184 logd("m_MoveType typedescription not found");
185 return false; 185 return false;
186 } 186 }
187 int best = 0; 187 int best = 0;
@@ -210,7 +210,7 @@ static bool resolve_absvelocity() {
210 break; 210 break;
211 } 211 }
212 if (!str_va) { 212 if (!str_va) {
213 logf("m_vecAbsVelocity string not found"); 213 logd("m_vecAbsVelocity string not found");
214 return false; 214 return false;
215 } 215 }
216 216
@@ -241,7 +241,7 @@ static bool resolve_absvelocity() {
241 } 241 }
242 } 242 }
243 if (ncand == 0) { 243 if (ncand == 0) {
244 logf("m_vecAbsVelocity typedescription not found"); 244 logd("m_vecAbsVelocity typedescription not found");
245 return false; 245 return false;
246 } 246 }
247 int best = 0; 247 int best = 0;
@@ -266,7 +266,7 @@ void resolve_netvars() {
266 classes++; 266 classes++;
267 collect_netvars(*(uintptr_t *)(cc + 0x0C), 0, 0); 267 collect_netvars(*(uintptr_t *)(cc + 0x0C), 0, 0);
268 } 268 }
269 logf("client classes walked: %d", classes); 269 logd("client classes walked: %d", classes);
270 270
271 if (g_net.movetype < 0 && !resolve_movetype()) { 271 if (g_net.movetype < 0 && !resolve_movetype()) {
272 // movetype unresolved — bhop still works, just no noclip/ladder guard 272 // movetype unresolved — bhop still works, just no noclip/ladder guard
diff --git a/src/server.cpp b/src/server.cpp
index cfb4dd1..115cd53 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -100,7 +100,7 @@ bool resolve_server_netvars() {
100 if (!g_server.valid()) { 100 if (!g_server.valid()) {
101 if (!fail_logged) { 101 if (!fail_logged) {
102 fail_logged = true; 102 fail_logged = true;
103 logf("server datamap: server.so not mapped yet"); 103 logd("server datamap: server.so not mapped yet");
104 } 104 }
105 return false; 105 return false;
106 } 106 }
diff --git a/src/vischeck.cpp b/src/vischeck.cpp
index 6d453c5..dcb5c3c 100644
--- a/src/vischeck.cpp
+++ b/src/vischeck.cpp
@@ -185,7 +185,7 @@ float ground_distance(const Vector &origin, void *ignore, float max_dist) {
185 static int solid_logs = 0; 185 static int solid_logs = 0;
186 if (tr.startsolid && solid_logs < 4) { 186 if (tr.startsolid && solid_logs < 4) {
187 solid_logs++; 187 solid_logs++;
188 logf("ground trace: startsolid at (%.1f %.1f %.1f)", origin.x, 188 logd("ground trace: startsolid at (%.1f %.1f %.1f)", origin.x,
189 origin.y, origin.z); 189 origin.y, origin.z);
190 } 190 }
191 return -1.0f; 191 return -1.0f;