Skip to content

Defer UI operations until UI system runs frame update#6789

Merged
metalgearsloth merged 1 commit into
space-wizards:masterfrom
eoineoineoin:eoin/20260619.deferui
Jul 16, 2026
Merged

Defer UI operations until UI system runs frame update#6789
metalgearsloth merged 1 commit into
space-wizards:masterfrom
eoineoineoin:eoin/20260619.deferui

Conversation

@eoineoineoin

Copy link
Copy Markdown
Member

Redux of Space-Wizards-Federation/RobustToolbox#50. There's a few more comments on that issue, but here's a copy-paste of the description:

In the UI system, some updates are applied immediately, while some are deferred until the frame update. This changes all the operations to be deferred. This fixes a UI bug, but there's a more fundamental underlying bug here.

First, the bug;

  1. forcemap Packed
  2. startround
  3. Find the R&D server, spawn some research point disks and add them to the server.
  4. Open the UI of the R&D computer beside the server and press the "Research" button on one of the technologies. You'll see the UI update - available points decrease, get a new entry in "Unlocked technologies" and you get new technology options to research.
  5. Close that UI.
  6. Open the UI of one of the other two R&D computer and press "Research" on one of the technologies. You don't get a proper UI update; the points decrease, but you don't get a new entry in the "Unlocked technologies" nor new technology options to research.
  7. Close the UI and open it again - it's now updated.

For clarification:
RNDComputers

Two important components here; the TechnologyDatabaseComponent stores the information on unlocked and available technologies, and the UserInterfaceComponent provides the UI (uh-huh). The ResearchConsoleBoundUserInterface.UpdateState calls an update on the ResearchConsoleMenu, which in turn reads the TechnologyDatabaseComponent to populate the UI. When you press the "Research" button, a message is sent to the server which causes it to Dirty() both of these components.

For the broken computers, the ClientGameStateManager.ApplyGameState applies the state to the UserInterfaceComponent before the TechnologyDatabaseComponent, but because the SharedUserInterfaceSystem has a ComponentHandleState subscription, it performs the work in that callback immediately so the UI ends up reading stale values. By deferring all the UI operations until the frame render, we know that any components the UI wants to read will have had their state applied.

I didn't look into why one of the computers seems to work - suspect it's simply serialization and the order of components stored in that map.

There seems to be a more fundamental issue here; because the component state updates are unordered, any time you have a system which subscribes to ComponentHandleState and reads from a different component, you're in danger of getting stale data. I did a quick scan over all the subscriptions in content and most look pretty benign, though it's gonna be a bunch of work to untangle, especially with systems which raise further events in that subscription. SharedSolutionContainerSystem looks suspicious, for example, while FixtureSystem (in engine) seems to have a much clearer issue if the updates arrive out of order.

@eoineoineoin
eoineoineoin requested a review from DrSmugleaf as a code owner July 13, 2026 21:14
@metalgearsloth
metalgearsloth merged commit f44d7e0 into space-wizards:master Jul 16, 2026
6 of 7 checks passed
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