Fix marker crash on logout and add module lifecycle table

The marker cleanup hook on CleanupWorldAndEntities was never installed —
markers.installHooks() was missing from install() in main.zig. Entities
created via WorldMarker were never cleaned up before the game's atexit
handler iterated the hash table over freed heap memory.

Key changes:
- Add markers.installHooks() call (the actual crash fix)
- Replace manual install/uninstall/shutdown lists with a single modules
  table that drives all three phases — prevents this class of bug
- Gate marker Lua functions, addon, and keybindings behind isActive()
  so they're skipped when another DLL owns the hooks
- Add world_cleanup_hook.detach() to removeHooks() (was missing)
- Migrate from vendored libs/hook to external zhook dependency
- Unify installHooks return types to void across all modules
- Add diagnostic logging to marker cleanup (temporary, for testing)
This commit is contained in:
MarcelineVQ
2026-03-02 07:34:18 -08:00
parent 43313f439d
commit 475070e910
25 changed files with 1793 additions and 1749 deletions
+8 -22
View File
@@ -1,5 +1,5 @@
const std = @import("std");
const hook = @import("hook");
const hook = @import("zhook");
const con = @import("../console.zig");
const WINAPI = std.builtin.CallingConvention.winapi;
@@ -385,25 +385,16 @@ pub fn lootAllCorpses(_: *anyopaque) callconv(.c) u32 {
// Per-frame hook for processing the loot queue.
// =============================================================================
var scene_end_hook = hook.Hook{};
fn hookSceneEnd(device: u32, _edx: u32) callconv(.c) void {
_ = _edx;
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
const SceneEndFn = fn (u32) callconv(tc) void;
var scene_end_hook: hook.Detour(SceneEndFn) = .{};
fn hookSceneEnd(device: u32) callconv(tc) void {
if (loot_active) {
processLootQueue();
}
callOriginalSceneEnd(device);
}
fn callOriginalSceneEnd(device: u32) void {
asm volatile (
\\call *%[func]
:
: [_] "{ecx}" (device),
[func] "r" (scene_end_hook.trampoline),
: .{ .eax = true, .edx = true, .memory = true, .cc = true });
scene_end_hook.callOriginal(.{device});
}
// =============================================================================
@@ -431,17 +422,12 @@ pub fn installHooks() void {
g_is_hook_owner = true;
// SceneEnd — per-frame loot queue processing
// Uses thunk: __fastcall(ECX=device, EDX) → cdecl(device, edx)
if (scene_end_hook.prepare(Offsets.ADDR_SceneEnd, 9, &.{})) {
const thunk = scene_end_hook.mem.? + 32;
_ = hook.buildFastcallToCdeclThunk(thunk, @intFromPtr(&hookSceneEnd), 0);
scene_end_hook.activate(@intFromPtr(thunk));
}
_ = scene_end_hook.attach(Offsets.ADDR_SceneEnd, &hookSceneEnd);
}
pub fn removeHooks() void {
if (g_is_hook_owner) {
scene_end_hook.remove();
scene_end_hook.detach();
}
if (g_is_hook_owner) {