Skip to content

TankUISubsystem Ready/Broadcast Flow

This doc summarizes how the multi-queue + flush pipeline works for widget controller readiness, including the split local vs simulated-proxy broadcasts.

Queues and meaning

  • PendingPlayerStates
  • Raw PlayerStateAdded events that arrive before the local player is ready.
  • Held so the local PlayerState can be identified and initialized first.
  • PendingPostNetInitPlayerStates
  • Remote PlayerStates that are not post-net-init yet (client only).
  • Waits on OnPostNetInitDelegate.
  • PendingUniqueIdPlayerStates
  • Remote PlayerStates that do not yet have a replicated UniqueId.
  • Waits on OnUniqueIdReadyDelegate.
  • PendingReadyBroadcastPlayerStates
  • Remote PlayerStates that are initialized (view models + controller ready), but their OnSimulatedProxyWidgetControllerReady broadcast is deferred until the local player is ready.

Core flow

1) HandlePlayerStateAdded - If local is not ready (bLocalReadyBroadcasted == false), the PlayerState is queued in PendingPlayerStates and initialization returns early. - Once local is ready, remotes can be initialized immediately.

2) ProcessPendingPlayerStates - Called when the local PlayerState becomes known. - Removes the local PlayerState from PendingPlayerStates. - Initializes the local view models with bDeferReadyBroadcast = true.

3) InitializeViewModelsForPlayerStateInternal - For local PlayerState: - Calls TryBroadcastLocalReady (which gates on EOS readiness). - Successful broadcast fires OnLocalWidgetControllerReady. - For remote PlayerState: - If local is not ready, it enqueues the PlayerState into PendingReadyBroadcastPlayerStates and returns (also queues UniqueId if needed). - If local is ready, it broadcasts immediately via OnSimulatedProxyWidgetControllerReady (or queues UniqueId if needed).

4) TryBroadcastLocalReady - When local passes EOS readiness checks, it broadcasts local ready. - Then it flushes: - FlushPendingPlayerStates (initialize any queued remotes) - FlushPendingReadyBroadcasts (emit deferred remote ready events)

5) FlushPendingReadyBroadcasts - For each queued remote PlayerState: - If still missing PostNetInit / UniqueId / or not initialized, it is re-queued. - Otherwise it broadcasts OnSimulatedProxyWidgetControllerReady.

Why this defers remote ready

Remote PlayerStates can become valid before the local overlay/controller exists. By deferring remote OnSimulatedProxyWidgetControllerReady until after local ready, the first broadcast observed by UI systems is guaranteed to be local-ready, and remote ready broadcasts are emitted after the local overlay controller exists.

HUD/Overlay Notes

  • ATankHUD::InitOverlay no longer creates the overlay controller; it expects one to already exist (typically created during InitializeViewModelsForPlayerStateInternal).
  • UI extensions are registered against the LocalPlayer context, not PlayerState. This keeps bindings stable across travel but means widgets are pooled/reused.
  • Because pooled widgets do not re-construct on reuse, refresh/rebind must happen in OnUIDataApplied / OnActivated, not just NativeConstruct.