Module control API: - Three cdecl exports: WeirdUtils_IsModuleActive, WeirdUtils_DisableModule, WeirdUtils_DisableAll - C header include/weirdutils_api.h for runtime DLL discovery - All modules gain pub module_name, isActive(), shared mutex via src/mutex.zig - build.zig refactored to module_list array of ModuleDesc Bigcursor: - D3D9 vtable hooks on SetCursorProperties/ShowCursor - Scale2x/Scale3x pixel-art upscalers in pure Zig (replaces 12K-line hqx C) - Bilinear resampler for fractional scales (1.0-4.0, default 1.2) - Win32 cursor via CreateIconIndirect bypasses D3D9 32x32 limit - FNV-1a hash cache for ~16 cursor bitmaps - Lua API: SetCursorScale(n) / GetCursorScale() - CVar cursorScale for Config.wtf persistence (tenths) Other: - Add dpslog module stub - Docs and README updates
5.4 KiB
Proper Object Registration Plan (for Claude)
Goal
Stop ad-hoc list insertion and implement a correct, engine-native object creation/registration path for client-side markers in WoW 1.12.1.
Success = marker is visible in-world, no crash on create/destroy/toggle, and lifecycle uses native manager functions (not manual pointer surgery as primary path).
Current Status Summary (Read First)
-
Crash fixed, visibility not fixed
- We eliminated crash caused by callback slot garbage (
obj+0x174) in manager iteration path. - Markers still invisible in both current test paths.
- We eliminated crash caused by callback slot garbage (
-
What was tried
- Path A: Allocate world object + attach model + manual insert into manager list (
0x00C7CAEC). - Path B:
CreateGameObject_WithProperties+ fallback manual insertion if unlinked. - Both now create/destroy cleanly, still not rendering.
- Path A: Allocate world object + attach model + manual insert into manager list (
-
Known engine behaviors
- Manager iterator (
AllocateGameObjectregion around0x00683F80) uses positions at+0x5C/+0x60/+0x68and calls render-management functions. - It may call callback pointer at
+0x174if non-zero. - List pointers can be low-bit tagged.
- Manager iterator (
-
Conclusion so far
- List manipulation and crash stability are mostly solved.
- Remaining blocker is likely incorrect native registration/state initialization, i.e. object not entering correct draw/classification path.
Mandatory Context Files
Read these before coding:
research/marker-visibility-debug-log.mddocs/M2_MODEL_SYSTEM.mdresearch/dynamic-object-research.mdsrc/markers/markers.zig- Crash reference:
/media/bigfaststore/games/twmoa_1172/Errors/2026-02-27 07.11.41 Crash.txt
Also review the CLI Ghidra skill:
~/.claude/skills/ghidra-cli-wow-re/SKILL.md
Rules of Engagement
- Do not treat manual list insertion as final architecture.
- Do not trust decompiler signatures alone. Verify calling conventions from bytes/prologue/RET cleanup.
- Prefer discovering native add/remove APIs used by real visible objects.
- Every hypothesis must have an observable test log.
- No broad refactors until registration path is proven.
Execution Plan
Phase 1 - Identify canonical native registration path
Task 1.1: Recover true iterator and linkage semantics
- Disassemble around
0x00683F80(and nearby helpers) in detail. - Map exactly how nodes are traversed and linked (including pointer tagging and indirection via globals like
0x00C7CAE4/0x00C7CAEC). - Produce pseudocode with verified offsets and conditions.
Deliverable: short technical note: "Manager traversal/link semantics".
Task 1.2: Find native insertion/removal helpers
- Find xrefs to
0x00C7CAECand offsets+0x16C/+0x170. - Identify functions that insert/remove objects in production code.
- Verify one candidate by callsite context (who calls it and when).
Deliverable: candidate helper function list with addresses + confidence.
Task 1.3: Find real creator path for visible world objects
- Trace from known visible object creators (game objects/doodads/spell visuals) to registration.
- Determine minimal required function chain from creation to render visibility.
Deliverable: canonical call chain diagram with function addresses.
Phase 2 - Build proper registration wrapper in markers module
Task 2.1: Implement native-path wrapper
- Add a wrapper that uses discovered native creator/registration APIs.
- Manual list insertion may remain only as fallback/diagnostic mode, not default.
Task 2.2: Lifecycle correctness
- Ensure destroy path uses matching native teardown/unlink APIs.
- Prevent double-unlink and callback garbage.
Task 2.3: Instrumentation
- Keep concise debug logs for:
- object pointer
- model pointer
- registration result path
- destroy path confirmation
Deliverable: code in src/markers/markers.zig with clear mode labels.
Phase 3 - Validation protocol (must pass)
Run and capture logs for:
/mark on- visually confirm marker appears
/mark off- repeat 5+ times
- mode switching stress (
testa/testbif retained)
Must pass:
- no crash
- no panic
- marker visible at player position
- clean destroy every cycle
If failed:
- report exact failing step, log lines, and next smallest probe
Expected Outputs from Claude
- Updated code implementing native registration path.
- Updated
research/marker-visibility-debug-log.mdwith:- what changed
- what was validated
- remaining unknowns (if any)
- Optional update to
docs/M2_MODEL_SYSTEM.mdfor newly confirmed function semantics. - Final summary:
- root cause
- final fix
- evidence (logs + addresses)
Suggested Command Workflow (CLI Ghidra)
Use headless scripts via:
/home/august/.claude/skills/ghidra-cli-wow-re/scripts/run-analysis.sh /tmp/analysis.py
Focus scripts on:
- xrefs to manager globals and list offsets
- disassembly around candidate insertion helpers
- call graph from visible object creation paths
Non-Goals (for this pass)
- New marker visuals
- Effects polish
- Optimizations unrelated to visibility/registration correctness
- Expanding to many marker types before first visible stable marker
Acceptance Criteria
- Marker visibility achieved using engine-native registration path (or proven minimal wrapper around it)
- No crash across repeated create/destroy
- Documentation updated with verified semantics
- Ad-hoc list surgery no longer required as primary behavior