From 7f60b75c93a6f1af0e152cbbda908adf697fe891 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Mon, 20 Jul 2026 01:59:24 -0400 Subject: [PATCH 1/8] store intermediate bui states --- .../Systems/SharedUserInterfaceSystem.cs | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index a476fa08fe4..7187ec93915 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -48,7 +48,7 @@ private enum QueuedUpdate /// /// Defer BUIs during state handling so client doesn't spam a BUI constantly during prediction. /// - private readonly List<(BoundUserInterface bui, QueuedUpdate updateType)> _queuedBuis = new(); + private readonly List<(BoundUserInterface bui, QueuedUpdate updateType, BoundUserInterfaceState? state)> _queuedBuis = new(); public override void Initialize() { @@ -91,9 +91,9 @@ public override void Initialize() SubscribeLocalEvent(OnActorShutdown); } - private void AddQueued(BoundUserInterface bui, QueuedUpdate type) + private void AddQueued(BoundUserInterface bui, QueuedUpdate type, BoundUserInterfaceState? state = null) { - _queuedBuis.Add((bui, type)); + _queuedBuis.Add((bui, type, state)); } /// @@ -312,7 +312,7 @@ protected void OnUserInterfaceShutdown(Entity ent, ref C DebugTools.Assert(!ent.Comp.Actors.ContainsKey(key)); } - DebugTools.Assert(ent.Comp.ClientOpenInterfaces.Values.All(x => _queuedBuis.Contains((x, QueuedUpdate.Close)))); + DebugTools.Assert(ent.Comp.ClientOpenInterfaces.Values.All(x => _queuedBuis.Contains((x, QueuedUpdate.Close, null)))); } private void OnUserInterfaceGetState(Entity ent, ref ComponentGetState args) @@ -500,7 +500,7 @@ private void OnUserInterfaceHandleState(Entity ent, ref if (!ent.Comp.ClientOpenInterfaces.TryGetValue(key, out var cBui) || !cBui.IsOpened) continue; - AddQueued(cBui, QueuedUpdate.ApplyState); + AddQueued(cBui, QueuedUpdate.ApplyState, buiState); } } @@ -528,7 +528,7 @@ private void EnsureClientBui(Entity entity, Enum key, In // Existing BUI just keep it. if (entity.Comp.ClientOpenInterfaces.TryGetValue(key, out var existing)) { - _queuedBuis.Remove((existing, QueuedUpdate.Close)); + _queuedBuis.Remove((existing, QueuedUpdate.Close, null)); return; } @@ -1110,28 +1110,38 @@ public override void Update(float frameTime) { if (_timing.IsFirstTimePredicted) { - foreach (var (bui, updateType) in _queuedBuis) + foreach (var (bui, updateType, state) in _queuedBuis) { - if (updateType == QueuedUpdate.Open || updateType == QueuedUpdate.ApplyState) + switch (updateType) { + case QueuedUpdate.Open: #if EXCEPTION_TOLERANCE try { #endif - if (updateType == QueuedUpdate.Open) - { - bui.Open(); - } - - if (UIQuery.TryComp(bui.Owner, out var uiComp)) - { - if (uiComp.States.TryGetValue(bui.UiKey, out var buiState)) + if (updateType == QueuedUpdate.Open) { - bui.State = buiState; - bui.UpdateState(buiState); - bui.Update(); + bui.Open(); } +#if EXCEPTION_TOLERANCE + } + catch (Exception e) + { + Log.Error( + $"Caught exception while attempting to create a BUI {bui.UiKey} with type {bui.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); } +#endif + break; + case QueuedUpdate.ApplyState: + if (state == null) + break; +#if EXCEPTION_TOLERANCE + try + { +#endif + bui.State = state; + bui.Update(); + bui.UpdateState(state); #if EXCEPTION_TOLERANCE } catch (Exception e) @@ -1140,10 +1150,9 @@ public override void Update(float frameTime) $"Caught exception while attempting to create a BUI {bui.UiKey} with type {bui.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); } #endif - } - // Close BUI - else - { + break; + case QueuedUpdate.Close: + default: if (UIQuery.TryComp(bui.Owner, out var uiComp)) { uiComp.ClientOpenInterfaces.Remove(bui.UiKey); @@ -1167,6 +1176,7 @@ public override void Update(float frameTime) $"Caught exception while attempting to dispose of a BUI {bui.UiKey} with type {bui.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); } #endif + break; } } From a3e35b240312886aae7f950735cddec59579baa9 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Mon, 20 Jul 2026 02:13:45 -0400 Subject: [PATCH 2/8] exception printout, remove redundant check --- .../GameObjects/Systems/SharedUserInterfaceSystem.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index 7187ec93915..b553f517334 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -1119,10 +1119,7 @@ public override void Update(float frameTime) try { #endif - if (updateType == QueuedUpdate.Open) - { - bui.Open(); - } + bui.Open(); #if EXCEPTION_TOLERANCE } catch (Exception e) @@ -1147,7 +1144,7 @@ public override void Update(float frameTime) catch (Exception e) { Log.Error( - $"Caught exception while attempting to create a BUI {bui.UiKey} with type {bui.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); + $"Caught exception while attempting to update a BUI {bui.UiKey} with type {bui.GetType()} with a state of type {state.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); } #endif break; From a89986f8601764ed66fec5ecb42837fcb494d3dc Mon Sep 17 00:00:00 2001 From: Whatstone Date: Mon, 20 Jul 2026 16:17:30 -0400 Subject: [PATCH 3/8] Set state on Open --- .../Systems/SharedUserInterfaceSystem.cs | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index b553f517334..4749fabb075 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -292,7 +292,10 @@ private void OnUserInterfaceStartup(Entity ent, ref Comp // PlayerAttachedEvent will catch some of these. foreach (var (key, bui) in ent.Comp.ClientOpenInterfaces) { - AddQueued(bui, QueuedUpdate.Open); + if (!ent.Comp.States.TryGetValue(key, out var state)) + state = null; + + AddQueued(bui, QueuedUpdate.Open, state); } } @@ -1112,44 +1115,34 @@ public override void Update(float frameTime) { foreach (var (bui, updateType, state) in _queuedBuis) { - switch (updateType) + if (updateType == QueuedUpdate.Open || updateType == QueuedUpdate.ApplyState) { - case QueuedUpdate.Open: #if EXCEPTION_TOLERANCE try { #endif - bui.Open(); -#if EXCEPTION_TOLERANCE - } - catch (Exception e) + if (updateType == QueuedUpdate.Open) { - Log.Error( - $"Caught exception while attempting to create a BUI {bui.UiKey} with type {bui.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); + bui.Open(); } -#endif - break; - case QueuedUpdate.ApplyState: - if (state == null) - break; -#if EXCEPTION_TOLERANCE - try + + if (state != null) { -#endif bui.State = state; - bui.Update(); bui.UpdateState(state); + bui.Update(); + } #if EXCEPTION_TOLERANCE } catch (Exception e) { Log.Error( - $"Caught exception while attempting to update a BUI {bui.UiKey} with type {bui.GetType()} with a state of type {state.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); + $"Caught exception while attempting to {updateType == QueuedUpdate.Open ? "create" : "update"} a BUI {bui.UiKey} with type {bui.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); } #endif - break; - case QueuedUpdate.Close: - default: + } + else // Close BUI + { if (UIQuery.TryComp(bui.Owner, out var uiComp)) { uiComp.ClientOpenInterfaces.Remove(bui.UiKey); @@ -1173,7 +1166,6 @@ public override void Update(float frameTime) $"Caught exception while attempting to dispose of a BUI {bui.UiKey} with type {bui.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); } #endif - break; } } From 30ac87b3cd1da3e6e44c40d21610fdf0ba7416b3 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Mon, 20 Jul 2026 16:20:07 -0400 Subject: [PATCH 4/8] trygetvalue defaults, you dingus --- .../GameObjects/Systems/SharedUserInterfaceSystem.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index 4749fabb075..e6b291af735 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -292,9 +292,7 @@ private void OnUserInterfaceStartup(Entity ent, ref Comp // PlayerAttachedEvent will catch some of these. foreach (var (key, bui) in ent.Comp.ClientOpenInterfaces) { - if (!ent.Comp.States.TryGetValue(key, out var state)) - state = null; - + ent.Comp.States.TryGetValue(key, out var state); AddQueued(bui, QueuedUpdate.Open, state); } } From 8333a4992b6b40855194047902d8fd73848fbd05 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Mon, 20 Jul 2026 16:26:05 -0400 Subject: [PATCH 5/8] fix string interpolation bug --- Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index e6b291af735..f1ca83b016d 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -1134,8 +1134,9 @@ public override void Update(float frameTime) } catch (Exception e) { + var operationType = updateType == QueuedUpdate.Open ? "create" : "update"; Log.Error( - $"Caught exception while attempting to {updateType == QueuedUpdate.Open ? "create" : "update"} a BUI {bui.UiKey} with type {bui.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); + $"Caught exception while attempting to {operationType} a BUI {bui.UiKey} with type {bui.GetType()} on entity {ToPrettyString(bui.Owner)}. Exception: {e}"); } #endif } From 8d8f637ef053209711c31e9335845110c63f0d29 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Wed, 22 Jul 2026 12:39:43 -0400 Subject: [PATCH 6/8] queue in SetUiState --- Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index f1ca83b016d..e4de4afe55f 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -767,8 +767,7 @@ public void SetUiState(Entity entity, Enum key, BoundUs { if (bui.State?.Equals(state) != true) { - bui.UpdateState(state); - bui.Update(); + AddQueued(bui, QueuedUpdate.ApplyState, state); } } From 59430ab2f7f3ff47048926557b5498fa85e420ec Mon Sep 17 00:00:00 2001 From: Whatstone Date: Wed, 22 Jul 2026 17:45:16 -0400 Subject: [PATCH 7/8] queue in SetUiState regardless --- .../GameObjects/Systems/SharedUserInterfaceSystem.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index e4de4afe55f..e2f19828e70 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -765,10 +765,7 @@ public void SetUiState(Entity entity, Enum key, BoundUs // Predict the change on client if (state != null && _netManager.IsClient && entity.Comp.ClientOpenInterfaces.TryGetValue(key, out var bui)) { - if (bui.State?.Equals(state) != true) - { - AddQueued(bui, QueuedUpdate.ApplyState, state); - } + AddQueued(bui, QueuedUpdate.ApplyState, state); } DirtyField(entity, nameof(UserInterfaceComponent.States)); From 32456aa2b2782088c75e4078d96cb0be67330e55 Mon Sep 17 00:00:00 2001 From: Whatstone Date: Wed, 22 Jul 2026 22:15:02 -0400 Subject: [PATCH 8/8] TryGetValue->GetValueOrDefault --- Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index e2f19828e70..42605e24821 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -292,7 +292,7 @@ private void OnUserInterfaceStartup(Entity ent, ref Comp // PlayerAttachedEvent will catch some of these. foreach (var (key, bui) in ent.Comp.ClientOpenInterfaces) { - ent.Comp.States.TryGetValue(key, out var state); + var state = ent.Comp.States.GetValueOrDefault(key); AddQueued(bui, QueuedUpdate.Open, state); } }