Skip to content

Commit

Permalink
BUI deferral tweaks (#5503)
Browse files Browse the repository at this point in the history
  • Loading branch information
metalgearsloth authored Jan 27, 2025
1 parent b9b8019 commit d9bf1d1
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 67 deletions.
6 changes: 4 additions & 2 deletions Robust.Client/UserInterface/UserInterfaceManager.Input.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,10 @@ private static void _doGuiInput(

private void _clearTooltip()
{
if (!_showingTooltip) return;
_resetTooltipTimer();

if (!_showingTooltip)
return;

if (_suppliedTooltip != null)
{
Expand All @@ -322,7 +325,6 @@ private void _clearTooltip()
}

CurrentlyHovered?.PerformHideTooltip();
_resetTooltipTimer();
_showingTooltip = false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,6 @@ public abstract class BoundUserInterface : IDisposable
/// </summary>
protected internal BoundUserInterfaceState? State { get; internal set; }

// Bandaid just for storage :)
/// <summary>
/// Defers state handling
/// </summary>
[Obsolete]
public virtual bool DeferredClose { get; } = true;

protected BoundUserInterface(EntityUid owner, Enum uiKey)
{
IoCManager.InjectDependencies(this);
Expand Down
134 changes: 76 additions & 58 deletions Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
private ActorRangeCheckJob _rangeJob;

/// <summary>
/// Defer closing BUIs during state handling so client doesn't spam a BUI constantly during prediction.
/// Defer BUIs during state handling so client doesn't spam a BUI constantly during prediction.
/// </summary>
private readonly List<BoundUserInterface> _queuedCloses = new();
private readonly List<(BoundUserInterface Bui, bool value)> _queuedBuis = new();

/// <summary>
/// Temporary storage for BUI keys
/// </summary>
private ValueList<Enum> _keys = new();

public override void Initialize()
{
Expand Down Expand Up @@ -227,13 +232,7 @@ private void CloseUiInternal(Entity<UserInterfaceComponent?> ent, Enum key, Enti

if (ent.Comp.ClientOpenInterfaces.TryGetValue(key, out var cBui))
{
if (cBui.DeferredClose)
_queuedCloses.Add(cBui);
else
{
ent.Comp.ClientOpenInterfaces.Remove(key);
cBui.Dispose();
}
_queuedBuis.Add((cBui, false));
}

if (ent.Comp.Actors.Count == 0)
Expand Down Expand Up @@ -275,13 +274,7 @@ private void OnUserInterfaceStartup(Entity<UserInterfaceComponent> ent, ref Comp
// PlayerAttachedEvent will catch some of these.
foreach (var (key, bui) in ent.Comp.ClientOpenInterfaces)
{
bui.Open();

if (ent.Comp.States.TryGetValue(key, out var state))
{
bui.UpdateState(state);
bui.Update();
}
_queuedBuis.Add((bui, true));
}
}

Expand All @@ -301,7 +294,7 @@ private void OnUserInterfaceShutdown(Entity<UserInterfaceComponent> ent, ref Com
DebugTools.Assert(!ent.Comp.Actors.ContainsKey(key));
}

DebugTools.Assert(ent.Comp.ClientOpenInterfaces.Values.All(x => _queuedCloses.Contains(x)));
DebugTools.Assert(ent.Comp.ClientOpenInterfaces.Values.All(x => _queuedBuis.Contains((x, false))));
}

private void OnUserInterfaceGetState(Entity<UserInterfaceComponent> ent, ref ComponentGetState args)
Expand Down Expand Up @@ -463,14 +456,7 @@ private void OnUserInterfaceHandleState(Entity<UserInterfaceComponent> ent, ref
}

var bui = ent.Comp.ClientOpenInterfaces[key];

if (bui.DeferredClose)
_queuedCloses.Add(bui);
else
{
ent.Comp.ClientOpenInterfaces.Remove(key);
bui.Dispose();
}
_queuedBuis.Add((bui, false));
}
}

Expand Down Expand Up @@ -527,9 +513,7 @@ private void EnsureClientBui(Entity<UserInterfaceComponent> entity, Enum key, In
// Existing BUI just keep it.
if (entity.Comp.ClientOpenInterfaces.TryGetValue(key, out var existing))
{
if (existing.DeferredClose)
_queuedCloses.Remove(existing);

_queuedBuis.Remove((existing, false));
return;
}

Expand All @@ -545,10 +529,6 @@ private void EnsureClientBui(Entity<UserInterfaceComponent> entity, Enum key, In
// Try-catch to try prevent error loops / bricked clients that constantly throw exceptions while applying game
// states. E.g., stripping UI used to throw NREs in some instances while fetching the identity of unknown
// entities.
#if EXCEPTION_TOLERANCE
try
{
#endif
var type = _reflection.LooseGetType(data.ClientType);
var boundUserInterface = (BoundUserInterface) _factory.CreateInstance(type, [entity.Owner, key]);
entity.Comp.ClientOpenInterfaces[key] = boundUserInterface;
Expand All @@ -557,23 +537,7 @@ private void EnsureClientBui(Entity<UserInterfaceComponent> entity, Enum key, In
if (!open)
return;

boundUserInterface.Open();

if (entity.Comp.States.TryGetValue(key, out var buiState))
{
boundUserInterface.State = buiState;
boundUserInterface.UpdateState(buiState);
boundUserInterface.Update();
}
#if EXCEPTION_TOLERANCE
}
catch (Exception e)
{
Log.Error(
$"Caught exception while attempting to create a BUI {key} with type {data.ClientType} on entity {ToPrettyString(entity.Owner)}. Exception: {e}");
return;
}
#endif
_queuedBuis.Add((boundUserInterface, true));
}

/// <summary>
Expand Down Expand Up @@ -887,6 +851,32 @@ public void ClientSendUiMessage(Entity<UserInterfaceComponent?> entity, Enum key
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(entity.Owner), message, key));
}

/// <summary>
/// Closes the user's UIs that match the specified key.
/// </summary>
public void CloseUserUis<T>(Entity<UserInterfaceUserComponent?> actor) where T: Enum
{
if (!UserQuery.Resolve(actor.Owner, ref actor.Comp, false))
return;

if (actor.Comp.OpenInterfaces.Count == 0)
return;

foreach (var (uid, enums) in actor.Comp.OpenInterfaces)
{
_keys.Clear();
_keys.AddRange(enums);

foreach (var weh in _keys)
{
if (weh is not T)
continue;

CloseUiInternal(uid, weh, actor.Owner);
}
}
}

/// <summary>
/// Closes all Uis for the actor.
/// </summary>
Expand All @@ -898,13 +888,12 @@ public void CloseUserUis(Entity<UserInterfaceUserComponent?> actor)
if (actor.Comp.OpenInterfaces.Count == 0)
return;

var enumCopy = new ValueList<Enum>();
foreach (var (uid, enums) in actor.Comp.OpenInterfaces)
{
enumCopy.Clear();
enumCopy.AddRange(enums);
_keys.Clear();
_keys.AddRange(enums);

foreach (var key in enumCopy)
foreach (var key in _keys)
{
CloseUiInternal(uid, key, actor.Owner);
}
Expand Down Expand Up @@ -1039,17 +1028,46 @@ public override void Update(float frameTime)
{
if (_timing.IsFirstTimePredicted)
{
foreach (var bui in _queuedCloses)
foreach (var (bui, open) in _queuedBuis)
{
if (UIQuery.TryComp(bui.Owner, out var uiComp))
if (open)
{
uiComp.ClientOpenInterfaces.Remove(bui.UiKey);
bui.Open();
#if EXCEPTION_TOLERANCE
try
{
#endif

if (UIQuery.TryComp(bui.Owner, out var uiComp))
{
if (uiComp.States.TryGetValue(bui.UiKey, out var buiState))
{
bui.State = buiState;
bui.UpdateState(buiState);
bui.Update();
}
}
#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
}
else
{
if (UIQuery.TryComp(bui.Owner, out var uiComp))
{
uiComp.ClientOpenInterfaces.Remove(bui.UiKey);
}

bui.Dispose();
bui.Dispose();
}
}

_queuedCloses.Clear();
_queuedBuis.Clear();
}

var query = AllEntityQuery<ActiveUserInterfaceComponent, UserInterfaceComponent>();
Expand Down

0 comments on commit d9bf1d1

Please sign in to comment.