The bind_! macro generated &mut self methods for vtable calls, giving
LLVM noalias guarantees that WA bridge calls violated. This caused
codegen-sensitive desyncs where unrelated code changes would shift
optimization decisions and expose the UB.
Fix: auto-generate _raw associated functions (taking raw pointers) for
every bind_! vtable method. Migrate all call sites in weapon dispatch,
weapon release, sound, cloud, and filter handlers to use _raw variants
and CTask::ddgame_raw() instead of the Task trait methods.
Also adds TeamMessage enum with NapalmStrike variant as the first typed
message, and CTaskTeam::on_napalm_strike handler (not yet wired up due
to a separate binary layout sensitivity issue under investigation).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: add Vtable marker trait for CTask<V> type safety
Add unsafe trait Vtable as a bound on CTask<V> and CGameTask<V>,
preventing arbitrary types from being used as vtable parameters.
- *const c_void implements Vtable (the default/untyped case)
- #[vtable] proc macro auto-generates `impl Vtable for *const MyVTable`
- CTask<V: Vtable> and CGameTask<V: Vtable> enforce the bound
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Make CTask generic over its vtable pointer type:
CTask<V: 'static = *const c_void>
This allows subclasses to specify their typed vtable:
CTaskTeam { base: CTask<*const CTaskTeamVTable>, ... }
Changes:
FieldRegistry derive macro (openwa-derive):
- Handle generic type params by substituting defaults in size_of
expressions. offset_of uses the bare struct name (Rust applies
defaults). Builds a generic_defaults map from TypeParam::default.
bind macro (openwa-derive):
- Accept dotted field paths ($($vtable_field:ident).+) instead of
single idents, enabling bind_VTable!(Struct, base.vtable) for
inherited vtables.
CTask (openwa-core):
- Add generic parameter V with default *const c_void. All existing
code using bare CTask continues to work unchanged.
CTaskTeam (openwa-core):
- Use CTask<*const CTaskTeamVTable> as base type
- Replace manual vtable() and handle_message() with bind macro
- bind_CTaskTeamVTable!(CTaskTeam, base.vtable) generates typed
method wrappers automati…
feat: upgrade debug tools with registry, typed reflection, and symbolic addresses
Wire the address registry, field registries, typed vtables, and live
object tracker into all debug tools:
- Watchpoint stack traces now show symbolicated function names via
format_va() instead of raw hex addresses
- Validation auto-discovers vtables from the registry and validates
all named slots from #[vtable(...)] structs
- New ValueKind enum + FieldFormatter trait for typed field formatting:
scalars as decimal, Fixed as float, pointers resolved via registry,
CString as quoted strings. Extensible via inventory::submit!
- Snapshot fallback uses FieldRegistry for entity types without
hand-coded Snapshot impls (CTaskTeam, CTaskMine, etc.)
- New debug CLI commands: inspect (typed struct read), objects (list
tracked live objects)
- Symbolic address expressions: ddgame, ddgame+field_name,
ddgame->field_name work in all CLI commands
- New protocol messages: Inspect, ListObjects, ResolveAlias, ResolveField
- #[field(kind = "CString")] attribute for string fields in FieldRegist…
feat: add #[vtable(...)] attribute macro for typed vtable definitions
The #[derive(FieldRegistry)] proc macro now also submits a
StructRegistration to the global inventory, enabling runtime lookup
by struct name or vtable address:
- struct_fields_for("DDGame") -> Option<&StructFields>
- struct_fields_for_vtable(0x0066_A30C) -> Option<&StructFields>
Uses a lazy HashMap<&str, &StructFields> built on first query.
This connects the field registry to detect_pointer() for future
"DDGame+0x45EC = rng_state" style output.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: correct misleading comment in FieldRegistry derive macro
New openwa-derive crate with FieldRegistry proc macro that generates
HasFieldRegistry impls for #[repr(C)] structs. Uses offset_of!() and
size_of() for compiler-verified offsets. Fields prefixed _unknown/_pad
are automatically excluded.
Applied to 5 key structs: DDGame, DDGameWrapper, GameSession, CTask,
CTaskWorm. Each now has a field_registry() -> &'static StructFields
method for runtime offset-to-name lookups.
This lays groundwork for typed vtable derive macros using the same
proc macro infrastructure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>