Game: RF 4.55 (Dragonborn), server v0.2.0, Windows build (stock gameserver.exe, SHA-256 96452fbd…).
Client: the supported Taiwan build, RF_Online.bin SHA-256 46528aac…2bbc558 (2025-07-16), Language=Taiwan.
Summary
I fire a launcher with area rockets (Nuclear, Cross, Electric, Venom or Cyclone; bulletitem.m_nEffectGroup 6)
into a group of monsters. Only the target seems to take the hit. Two separate server bugs cause this:
- The splash radius is the smallest one. A normal attack always asks for area level 0, radius 42, so a monster standing next to the target is usually outside it or takes 0–5% of the hit.
- When the splash does land, the client can't show it. The attack result packs each target in 10 bytes and the client reads 14, so every target after the first is garbage on screen. The server damages them, but the player sees no number and no hit. This affects every multi-target result: area forces and skills too.
Fixing only (1) still looks broken to the player because of (2), so they're reported together.
1. Normal attacks always use area level 0 (radius 42)
Chain: handleAttack 0x1407b7700 → meleeSpreadSwing 0x140877a00 → aoeTargets 0x14075e2e0.
meleeSpreadShape 0x140878660 picks the shape (5 cone, 6 circle) from the bullet's m_nEffectGroup, and the call
then passes a constant level:
140877b44: mov r9d, eax ; shape 5/6
140877b47: xor r10d, r10d ; level 0
140877b4a: movsd xmm2, [0x140d1a9b8] ; cone angle 90
140877b52: mov r11d, 1 ; centre on the target
140877b60: call aoeTargets
aoeTargets takes the radius from {42, 56, 70, 84}[level] (0x1412c0ca0 for shape 6, 0x1412c0c80 for 5). It keeps targets whose centre-to-centre distance is under the radius and weights each by (r − d) / r.- Most field monsters are 40–60 wide (
m_fWidth), so a neighbour touching the target is 40–60 away: outside the radius or at 0–5%. - Forces pass their grade and skills their level, so their radius grows. Bullets never do.
- The client has the same table (
0xaa9acc, 0xaaaf70, 0xaab1d4) indexed by skill/force grade, and no radius for bullets, so the server alone decides. - Cone angle (shape 5): the client reads
{15, 30, 45, 60} by grade (0xaaaf80); the server uses a fixed 90 for normal attacks (0x140d1a9b8) and 180 for forces (0x140d1a9d8).
Seen: Soul launcher of Returnee + Giga Nuclear Rocket on Crawler Bunch and Ops Lava: one kill per volley. The
only multi-kill in a session was two overlapping Ops Lava (monster killed 2026-10-06 22:12:56, serials 1002716
and 1002717).
2. Attack results pack 10-byte targets; the client reads 14
- Server:
protocol.AttackGenResult 0x140643020 writes 11 + 10n bytes. Each target is u8 kind, u32 serial, u16 damage, u8 flag, u16. The same layout is in AttackSkillResult 0x1406433e0, AttackForceResult 0x1406438e0, AttackSiegeResult 0x14065aa60, AttackTrapInform 0x14065e140 and AttackUnitResult 0x140665060. - Client: every attack-result handler (05/07
0x5a4a10, 05/08 0x5a4c50, 05/09 0x5a4eb0, 05/0A, 05/0B, 05/19, 05/7A, 05/97, 05/98) calls the target-list parser 0x5a42d0, which steps 14 bytes (imul eax,eax,0xe): u8 kind, u32 serial, u32 damage (+5), u8 (+9), u32 (+0xA). - With one target it happens to read right. From the second target on, each entry is 4·i bytes off, so kind, serial and damage are garbage and the serial matches no monster.
A read-only client hook logging each 05/07 both ways (server's 10-byte view, client's 14-byte view):
05/07 gen from 0:3162, 3 target(s) | server 1:1005299=234 1:1005304=217 1:1005300=188 | client 1:1005299=234 0:217=1458831616
00 5A 0C 00 00 00 0F 01 00 00 03 01 F3 56 0F 00 EA 00 00 00 00 01 F8 56 0F 00 D9 00 00 00 00 01 F4 56 0F 00 BC 00 00 00 00
05/07 gen from 0:3162, 3 target(s) | server 1:1030641=408 1:1030642=221 1:1030643=1 | client 1:1030641=408 0:221=3119710464
The second and third monsters do lose HP; only the first is drawn.
RF455-046 (the 65534 block number) mentions the 14-byte layout as one way to fix it. If that fix already widened