Skip to content

Record one warp attempt per call in CLIENTS_NotifyWarpAttempt - #252

Open
ResurrectedTrader wants to merge 1 commit into
ThePhrozenKeep:masterfrom
ResurrectedTrader:fix/clients-warp-attempt-single-slot
Open

ResurrectedTrader wants to merge 1 commit into
ThePhrozenKeep:masterfrom
ResurrectedTrader:fix/clients-warp-attempt-single-slot

Conversation

@ResurrectedTrader

Copy link
Copy Markdown
Contributor

CLIENTS_NotifyWarpAttempt writes the current frame into every empty or expired slot of aLastWarpAttemptsFrame, not just the first one. One sync warp on a fresh client therefore fills all five slots, and the next CLIENTS_ShouldDelayWarpAttempt sees five recent attempts and delays the warp after one sync warp instead of five.

1.10f

The whole of D2Game.0x6FC34700 (edi = pGame, [edi+0xa8] = dwGameFrame, eax = pClient, +0x3c0 = aLastWarpAttemptsFrame[5]). The loop moves past slots that are set and within 2250 frames (0x8ca, 90 * DEFAULT_FRAMES_PER_SECOND). The first empty or expired slot exits the loop, gets the current frame, and the function returns:

6FC34700  push    ebx
6FC34701  mov     eax, edx
6FC34703  push    esi
6FC34704  push    edi
6FC34705  test    eax, eax                          ; pUnit && dwUnitType == UNIT_PLAYER
6FC34707  mov     edi, ecx
6FC34709  je      0x6fc34710
6FC3470B  cmp     dword ptr [eax], 0
6FC3470E  je      0x6fc3472e
6FC34710  push    0xcd3
6FC34715  push    0x6fd2c350
6FC3471A  push    0x6fd2c99c
6FC3471F  call    0x6fd1c6ee
6FC34724  add     esp, 0xc
6FC34727  push    -1
6FC34729  call    0x6fd1c7ed
6FC3472E  push    0xcd5
6FC34733  mov     edx, 0x6fd2c350
6FC34738  mov     ecx, eax
6FC3473A  call    0x6fcbc2e0                        ; SUNIT_GetClientFromPlayer
6FC3473F  test    eax, eax
6FC34741  jne     0x6fc34761
6FC34743  push    0xcd6
6FC34748  push    0x6fd2c350
6FC3474D  push    0x6fd2c33c
6FC34752  call    0x6fd1c6ee
6FC34757  add     esp, 0xc
6FC3475A  push    -1
6FC3475C  call    0x6fd1c7ed
6FC34761  xor     edx, edx
6FC34763  lea     esi, [eax + 0x3c0]                ; aLastWarpAttemptsFrame
6FC34769  mov     ecx, dword ptr [esi]              ; slot
6FC3476B  test    ecx, ecx
6FC3476D  je      0x6fc3478c                        ; <== empty -> write this slot
6FC3476F  mov     ebx, dword ptr [edi + 0xa8]
6FC34775  sub     ebx, ecx                          ; dwGameFrame - slot
6FC34777  cmp     ebx, 0x8ca
6FC3477D  ja      0x6fc3478c                        ; <== expired (> 2250) -> write this slot
6FC3477F  inc     edx                               ; recent: next slot
6FC34780  add     esi, 4
6FC34783  cmp     edx, 5                            ; 5 slots
6FC34786  jl      0x6fc34769
6FC34788  pop     edi
6FC34789  pop     esi
6FC3478A  pop     ebx
6FC3478B  ret                                       ; all five recent: nothing written
6FC3478C  mov     ecx, dword ptr [edi + 0xa8]
6FC34792  pop     edi
6FC34793  pop     esi
6FC34794  mov     dword ptr [eax + edx*4 + 0x3c0], ecx ; <== write the one slot ...
6FC3479B  pop     ebx
6FC3479C  ret                                       ; <== ... and return

1.14d

The same in Game.exe 0x539360:

00539360  push    ebx
00539361  push    esi
00539362  mov     eax, edx
00539364  test    eax, eax
00539366  push    edi
00539367  mov     edi, ecx
00539369  jz      short loc_539370
0053936B  cmp     dword ptr [eax], 0
0053936E  jz      short loc_53938F
00539370  push    0D1Bh
00539375  call    GetAddress
0053937A  push    eax
0053937B  push    offset Default
00539380  call    ERROR_UnrecoverableInternalError_Halt
00539385  add     esp, 0Ch
00539388  push    0FFFFFFFFh
0053938A  call    _exit
0053938F  push    0D1Dh
00539394  mov     edx, offset aGameClientsCpp
00539399  mov     ecx, eax
0053939B  call    GetClient
005393A0  test    eax, eax
005393A2  jnz     short loc_5393AB
005393A4  push    0D1Eh
005393A9  jmp     short loc_539375
005393AB  xor     ecx, ecx
005393AD  lea     esi, [eax+3C0h]                   ; aLastWarpAttemptsFrame
005393B3  mov     edx, [esi]
005393B5  test    edx, edx
005393B7  jz      short loc_5393D8                  ; <== empty -> write this slot
005393B9  mov     ebx, [edi+0A8h]
005393BF  sub     ebx, edx
005393C1  cmp     ebx, 8CAh                         ; dwGameFrame - slot > 2250
005393C7  ja      short loc_5393D8                  ; <== expired -> write this slot
005393C9  add     ecx, 1
005393CC  add     esi, 4
005393CF  cmp     ecx, 5                            ; 5 slots
005393D2  jl      short loc_5393B3
005393D4  pop     edi
005393D5  pop     esi
005393D6  pop     ebx
005393D7  retn
005393D8  mov     edx, [edi+0A8h]
005393DE  pop     edi
005393DF  pop     esi
005393E0  mov     [eax+ecx*4+3C0h], edx             ; <== write the one slot ...
005393E7  pop     ebx
005393E8  retn                                      ; <== ... and return

Change

break after recording the attempt, and add the 1.14d addresses of CLIENTS_NotifyWarpAttempt (0x00539360) and CLIENTS_ShouldDelayWarpAttempt (0x005393F0). Not version-gated, since 1.10f and 1.14d agree.

🤖 Generated with Claude Code

CLIENTS_NotifyWarpAttempt wrote the current frame into every empty or
expired slot of aLastWarpAttemptsFrame instead of only the first one.
A single attempt could fill all five slots, so the next
CLIENTS_ShouldDelayWarpAttempt saw five recent attempts and delayed the
warp after one sync warp instead of after five.

1.10f (D2Game.0x6FC34700) walks the slots, skips the ones that are set
and within 2250 frames (0x6FC34769-0x6FC34786), and writes only the
first empty or expired one before returning (0x6FC3478C-0x6FC3479C).
1.14d (Game.exe 0x539360) is the same.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant