Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUI deferral tweaks #5503

Merged
merged 9 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading