Skip to content

fix: cleanupImmediately on one unit deletes others - #3311

Open
efrec wants to merge 1 commit into
beyond-all-reason:masterfrom
efrec:fix/cleanup-immediately-bug
Open

fix: cleanupImmediately on one unit deletes others#3311
efrec wants to merge 1 commit into
beyond-all-reason:masterfrom
efrec:fix/cleanup-immediately-bug

Conversation

@efrec

@efrec efrec commented Sep 1, 2026

Copy link
Copy Markdown

GarbageCollectUnit will drain unitsToBeRemoved to get one unit. The queue is filled at the end of one update and drained at the start of the next, but GameFrame runs before unitHandler.Update, so the queue is non-empty when deleting here with cleanupImmediately. Real Q: Do any current games have this bug?

That tripped the assert in debug builds. In release, it would delete every other queued unit a frame early. A very mysterious-seeming bug from the receiving end probably.

ParseUnit does not reject dead units, so maybe this would queue a unit a second time and DeleteUnit on the same pointer twice. Null checks are added against dereferencing in case.

GarbageCollectUnit will drain unitsToBeRemoved to get one unit. The queue is filled at the end of one update and drained at the start of the next, but GameFrame runs before unitHandler.Update, so the queue is non-empty when deleting here with cleanupImmediately. Real Q: Do any current games have this bug?

That tripped the assert in debug builds. In release, it would delete every other queued unit a frame early. A very mysterious-seeming bug from the receiving end probably.

ParseUnit does not reject dead units, so maybe this would queue a unit a second time and DeleteUnit on the same pointer twice. Null checks are added against dereferencing in case.
@sprunk

sprunk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Looks decent.

Real Q: Do any current games have this bug? (...) In release, it would delete every other queued unit a frame early. A very mysterious-seeming bug from the receiving end probably.

The engine technically doesn't make guarantees about when exactly units that finish are cleaned up, and there is an event (RenderUnitDestroyed) when that happens. As long as there aren't obvious visual issues (e.g. a frame where the unit still renders but fails to receive shaders) it should be fine.

@efrec

efrec commented Sep 2, 2026

Copy link
Copy Markdown
Author

Ideally there is some guarantee for "cleanupImmediately", which does specify the "when". I think that is a functioning guarantee, though, and just has this one bug, calling DestroyUnit(cleanupImmediately=true) inside of GameFrame specifically.

Maybe a unit script could synchronously delete itself through here, too, which may be a UAF, but the removal does occur. Seems correct.

I will take a look at the pipeline from this change to the render update but it seems to me that it would be unaffected.

@sprunk

sprunk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Ideally there is some guarantee for "cleanupImmediately", which does specify the "when". I think that is a functioning guarantee

Yes, I meant all the N-1 units that die normally via damage etc (as opposed to the one that is being removed with explicit cleanupImmediately from Lua) who would be getting cleaned up a frame early, who don't have a guarantee.

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.

2 participants