Repository navigation
Fix crashes around imported and freed TXDs - #5478
Flashmyname wants to merge 10 commits into
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unsigned reference-counter underflow and stale model bookkeeping can still cause incorrect TXD releases and prevent re-imports.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Fixes TXD lifetime and reference accounting to prevent crashes when imported textures or custom TXD slots are released.
Changes:
- Tracks TXD references taken while replacing atomics.
- Cleans replacement textures before destroying custom TXDs.
- Restores model TXD mappings and distinguishes internal model references.
Commit note: Include the motivation, reference-lifetime reasoning, and manual test results in the commit message.
| File | Description |
|---|---|
Client/sdk/game/CRenderWare.h |
Exposes TXD cleanup API. |
Client/mods/deathmatch/logic/CClientTXD.cpp |
Excludes model 0 from ped TXD imports. |
Client/mods/deathmatch/logic/CClientModel.cpp |
Cleans and restores models using freed TXDs. |
Client/game_sa/CTxdPoolSA.cpp |
Cleans streaming/default state before slot release. |
Client/game_sa/CRenderWareSA.TextureReplacing.cpp |
Detaches replacement textures from destroyed TXDs. |
Client/game_sa/CRenderWareSA.h |
Declares TXD cleanup implementation. |
Client/game_sa/CRenderWareSA.cpp |
Releases only TXD refs actually acquired. |
Client/game_sa/CModelInfoSA.h |
Adds internal-reference tracking and cleanup API. |
Client/game_sa/CModelInfoSA.cpp |
Adjusts TXD transfers for internally held refs. |
Client/game_sa/CFileLoaderSA.h |
Replaces atomic-consumption state with reference state. |
Client/game_sa/CFileLoaderSA.cpp |
Records when SetAtomic takes a TXD ref. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
14ef75d to
d6800fe
Compare
|
Copilot's wrong on the first one, the wrap is intended and the count stays exact. Changed it to int16 anyway so it doesn't look like a bug. usedInModelIds isn't a crash and master does the same. |


Summary
A TXD could be destroyed or freed while
engineImportTXDtextures were still in it, and a TXD lookup could unload a model that was still in use. Both crashed the client in several places._damatomics released a TXD ref thatSetDamagedAtomicnever takes.engineSetModelTXDIDmoved MTA's own model refs as TXD refs.engineResetModelTXDID.engineImportTXDright afterengineSetModelTXDIDon a streamed in vehicle crashed: to load the new TXD it unloaded the model the vehicle was still using. Same afterengineFreeTXDputs a model back on its own TXD.engineGetModelTextureNamesandengineGetModelTexturesdid the same.Motivation
Fixes #4028. Follows #5356, #4043 and #5042.
I also have fixes ready for the usedInModelIds issue Copilot pointed out and for a model ref leak. Both build on this PR, so I'll open them once this one is merged.
Test plan
txd4028.zip
Start the
txd4028resource and run the commands in one session,/rtbqfirst. On master reconnect after each crash./damtxd/rtbv/rta0x731A4Ein~CClientTXD/rtastop, thenquit~CClientTXD/rtbs0/rtbrf0x731A15/rtbq0x1566544/rtbcj, then/rtbcjw0x5A6C71/rtg10x5344E3The dump faults one step earlier, at
RwTexDictionaryRemoveTexture+0x6, because the dead texture's memory was already released there. With it made unreadable, master gives the dump's stack onquitand the fix exits cleanly.Steps
Then make the freed texture blocks unreadable (
VirtualProtectEx,PAGE_NOACCESS; on the fix build skip each block's first and last page) andquit.The texture lookups need no resource, just
runcode. Master crashes at0x5344E3, with the fix it prints 0 and a table.Checklist