I hope this turns out to be useful to someone here. I'm building a small LAN
server with bots for me and a few friends, ran into the coop crash everyone
knows about, and ended up chasing it a lot further than I meant to. Posting it
in case it helps the community — and honestly in the hope that between us we
can turn it into something properly useful, because I've taken it about as far
as I can on my own.
Short version: the BF2 dedicated server doesn't exit on purpose when it dies in
coop — it crashes, and the engine catches its own access violation with an
__except wrapped around the whole game loop, then returns the constant 42. That
is why nothing is ever logged: no dialog, no Event Log entry, no WER, and the
dmp folder stays empty.
The bug underneath is in AIDLL_w32ded.dll. A pathfinder lookup indexes an array
with -1 — the engine's own "not found" value — so it reads the heap block header
sitting in front of the vector's buffer and hands that back as an object pointer.
Dereferencing it is the crash. Nothing is corrupted; the container is intact.
A sibling function sixty bytes away in the same file checks for -1 twice before
doing the identical indexing.
Measured on retail 1.5.3153-802.0, gpm_coop, 32 bots, same map both ways:
without the workaround: 47.7 / 60.9 / 83.2 / 155.7 s — median 72 s,
zero rounds completed out of four
with it: 17.4 minutes, two full rounds, zero exceptions
Full write-up with byte offsets, the disassembly and buildable code:
https://github.com/ShuhartProfit8ird/bf2-coop-crash
Two things you can do without taking my word for it:
1. Add "+seh 0" to your server command line. That switch exists in the retail
binary and disables the top-level __except. The next crash then becomes an
ordinary unhandled exception — WER fires, an event appears, and
"procdump -ma -e -w bf2_w32ded.exe" gets you a full dump with a stack. One
round tells you whether you're hitting this defect or a different one.
2. Check the byte signature at AIDLL_w32ded.dll+0x167AE0:
55 8B EC 57 8B 79 7C 85 FF 75 07 32 C0
If it matches, you have the same code.
Caveats, stated up front: this is a workaround, not a fix — the missing check is
still missing, and what makes that first "-1" appear two or three minutes in is
still open. I tested on a vanilla 1.5 dedicated server; I have NOT tested this
on PR or any other mod, so I don't know whether you hit the same path. The
signature check above answers that in a few seconds.
I'm not a programmer — this was found with an AI assistant reading the
disassembly, and everything is either a byte offset you can verify or a
measurement described precisely enough to repeat. Corrections very welcome,
especially from anyone who's been running these servers longer than I have.