paavohtl Claude Opus 4.6 (1M context) commited on
Commit
c7eda22
·
1 Parent(s): d40f088

fix: make FireWeapon hook a passthrough to fix gameplay desync

Browse files

The Rust FireWeapon dispatch logic has an undiagnosed bug that causes
replay desyncs on weapon use (exposed by longbow_water replay). Made
the trampoline a jmp-to-original passthrough until the dispatch bug
is found. Dispatch functions kept with #![allow(dead_code)] for future
debugging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-wormkit/src/replacements/weapon.rs CHANGED
@@ -3,6 +3,12 @@
3
  //! Replaces WA.exe functions that manage weapon ammo in the TeamArenaState area (DDGame + 0x4628):
4
  //! - GetAmmo (0x5225E0): query ammo count with delay/phase checks
5
  //! - AddAmmo (0x522640): add ammo to a weapon slot
 
 
 
 
 
 
6
  //! - SubtractAmmo (0x522680): decrement ammo count
7
  //! - CountAliveWorms (0x5225A0): check if >1 worm alive on team
8
  //! - FireWeapon (0x51EE60): passthrough with weapon type logging
@@ -149,34 +155,15 @@ usercall_trampoline!(fn trampoline_count_alive_worms; impl_fn = count_alive_worm
149
 
150
  static ORIG_FIRE_WEAPON: AtomicU32 = AtomicU32::new(0);
151
 
152
- /// Naked trampoline for FireWeapon.
153
- /// Must save ALL callee-saved registers (ESI, EDI, EBX, EBP) because the
154
- /// Rust cdecl impl may clobber them, and WeaponRelease (our caller) depends
155
- /// on them being preserved.
156
  #[unsafe(naked)]
157
  unsafe extern "C" fn trampoline_fire_weapon() {
158
  core::arch::naked_asm!(
159
- // Save all callee-saved + EDX
160
- "push ebx",
161
- "push esi",
162
- "push edi",
163
- "push ebp",
164
- "push edx",
165
- // Push cdecl args: (weapon_ctx=EAX, local_struct=ECX, worm)
166
- // Stack: 5 pushes (20) + ret (4) = 24 to stack param
167
- "push [esp+24]", // worm
168
- "push ecx", // local_struct
169
- "push eax", // weapon_ctx
170
- "call {impl_fn}",
171
- "add esp, 12",
172
- // Restore everything
173
- "pop edx",
174
- "pop ebp",
175
- "pop edi",
176
- "pop esi",
177
- "pop ebx",
178
- "ret 0x4",
179
- impl_fn = sym fire_weapon_impl,
180
  );
181
  }
182
 
@@ -571,26 +558,10 @@ unsafe extern "C" fn fire_worm_vtable_0xe(_worm: u32, _msg_id: u32) {
571
  pub fn install() -> Result<(), String> {
572
  unsafe {
573
  let _ = hook::install("AddAmmo", va::ADD_AMMO, trampoline_add_ammo as *const ())?;
574
-
575
  let _ = hook::install("GetAmmo", va::GET_AMMO, trampoline_get_ammo as *const ())?;
576
-
577
- let _ = hook::install(
578
- "SubtractAmmo",
579
- va::SUBTRACT_AMMO,
580
- trampoline_subtract_ammo as *const (),
581
- )?;
582
-
583
- let _ = hook::install(
584
- "CountAliveWorms",
585
- va::COUNT_ALIVE_WORMS,
586
- trampoline_count_alive_worms as *const (),
587
- )?;
588
-
589
- let trampoline = hook::install(
590
- "FireWeapon",
591
- va::FIRE_WEAPON,
592
- trampoline_fire_weapon as *const (),
593
- )?;
594
  ORIG_FIRE_WEAPON.store(trampoline as u32, Ordering::Relaxed);
595
  }
596
 
 
3
  //! Replaces WA.exe functions that manage weapon ammo in the TeamArenaState area (DDGame + 0x4628):
4
  //! - GetAmmo (0x5225E0): query ammo count with delay/phase checks
5
  //! - AddAmmo (0x522640): add ammo to a weapon slot
6
+ //!
7
+ //! NOTE: FireWeapon dispatch is currently a passthrough (jmp to original) due to
8
+ //! an undiagnosed desync bug in the Rust dispatch logic. The dispatch functions
9
+ //! are kept for future debugging. See project_ddgame_desync.md.
10
+
11
+ #![allow(dead_code)]
12
  //! - SubtractAmmo (0x522680): decrement ammo count
13
  //! - CountAliveWorms (0x5225A0): check if >1 worm alive on team
14
  //! - FireWeapon (0x51EE60): passthrough with weapon type logging
 
155
 
156
  static ORIG_FIRE_WEAPON: AtomicU32 = AtomicU32::new(0);
157
 
158
+ /// Naked trampoline for FireWeapon — passthrough to original.
159
+ ///
160
+ /// Convention: usercall(EAX=entry, ECX=local_struct) + stack(worm), RET 0x4.
161
+ /// Just jumps to the original — no Rust dispatch.
162
  #[unsafe(naked)]
163
  unsafe extern "C" fn trampoline_fire_weapon() {
164
  core::arch::naked_asm!(
165
+ "jmp [{orig}]",
166
+ orig = sym ORIG_FIRE_WEAPON,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  );
168
  }
169
 
 
558
  pub fn install() -> Result<(), String> {
559
  unsafe {
560
  let _ = hook::install("AddAmmo", va::ADD_AMMO, trampoline_add_ammo as *const ())?;
 
561
  let _ = hook::install("GetAmmo", va::GET_AMMO, trampoline_get_ammo as *const ())?;
562
+ let _ = hook::install("SubtractAmmo", va::SUBTRACT_AMMO, trampoline_subtract_ammo as *const ())?;
563
+ let _ = hook::install("CountAliveWorms", va::COUNT_ALIVE_WORMS, trampoline_count_alive_worms as *const ())?;
564
+ let trampoline = hook::install("FireWeapon", va::FIRE_WEAPON, trampoline_fire_weapon as *const ())?;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
  ORIG_FIRE_WEAPON.store(trampoline as u32, Ordering::Relaxed);
566
  }
567