From 176ca6c5784b3df2cea7a58031a77be3f369a189 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 20 Jul 2024 14:50:15 +1000 Subject: [PATCH] Add window helper for BUIs (#5183) * Add window helper for BUIs Automatically does OnClose and just makes content slightly nicer. * more * Add prototype reload helper * Add Box2i Center * weh --- .../EntitySystems/UserInterfaceSystem.cs | 30 +++++++ .../UserInterface/BoundUserInterfaceExt.cs | 33 +++++++ Robust.Shared.Maths/UIBox2i.cs | 2 + .../UserInterface/BoundUserInterface.cs | 27 ++++++ .../Systems/SharedUserInterfaceSystem.cs | 87 ++++++++++--------- 5 files changed, 137 insertions(+), 42 deletions(-) create mode 100644 Robust.Client/UserInterface/BoundUserInterfaceExt.cs diff --git a/Robust.Client/GameObjects/EntitySystems/UserInterfaceSystem.cs b/Robust.Client/GameObjects/EntitySystems/UserInterfaceSystem.cs index 20a0225a6..7c290cb4e 100644 --- a/Robust.Client/GameObjects/EntitySystems/UserInterfaceSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/UserInterfaceSystem.cs @@ -1,8 +1,38 @@ using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; namespace Robust.Client.GameObjects; public sealed class UserInterfaceSystem : SharedUserInterfaceSystem { + public override void Initialize() + { + base.Initialize(); + ProtoManager.PrototypesReloaded += OnProtoReload; + } + public override void Shutdown() + { + base.Shutdown(); + ProtoManager.PrototypesReloaded -= OnProtoReload; + } + + private void OnProtoReload(PrototypesReloadedEventArgs obj) + { + var player = Player.LocalEntity; + + if (!UserQuery.TryComp(player, out var userComp)) + return; + + foreach (var uid in userComp.OpenInterfaces.Keys) + { + if (!UIQuery.TryComp(uid, out var uiComp)) + continue; + + foreach (var bui in uiComp.ClientOpenInterfaces.Values) + { + bui.OnProtoReload(obj); + } + } + } } diff --git a/Robust.Client/UserInterface/BoundUserInterfaceExt.cs b/Robust.Client/UserInterface/BoundUserInterfaceExt.cs new file mode 100644 index 000000000..eb59385a3 --- /dev/null +++ b/Robust.Client/UserInterface/BoundUserInterfaceExt.cs @@ -0,0 +1,33 @@ +using System; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.GameObjects; + +namespace Robust.Client.UserInterface; + +public static class BoundUserInterfaceExt +{ + /// + /// Helper method to create a window and also handle closing the BUI when it's closed. + /// + public static T CreateWindow(this BoundUserInterface bui) where T : BaseWindow, new() + { + var window = bui.CreateDisposableControl(); + window.OpenCentered(); + window.OnClose += bui.Close; + return window; + } + + /// + /// Creates a control for this BUI that will be disposed when it is disposed. + /// + /// + /// + /// + public static T CreateDisposableControl(this BoundUserInterface bui) where T : Control, IDisposable, new() + { + var control = new T(); + bui.Disposals ??= []; + bui.Disposals.Add(control); + return control; + } +} diff --git a/Robust.Shared.Maths/UIBox2i.cs b/Robust.Shared.Maths/UIBox2i.cs index 91db90dd6..353b9f6f0 100644 --- a/Robust.Shared.Maths/UIBox2i.cs +++ b/Robust.Shared.Maths/UIBox2i.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Robust.Shared.Utility; @@ -22,6 +23,7 @@ namespace Robust.Shared.Maths public readonly int Width => Math.Abs(Right - Left); public readonly int Height => Math.Abs(Top - Bottom); public readonly Vector2i Size => new(Width, Height); + public readonly Vector2 Center => TopRight - BottomLeft / 2; public UIBox2i(Vector2i topLeft, Vector2i bottomRight) { diff --git a/Robust.Shared/GameObjects/Components/UserInterface/BoundUserInterface.cs b/Robust.Shared/GameObjects/Components/UserInterface/BoundUserInterface.cs index 3e09ae86c..c80debab8 100644 --- a/Robust.Shared/GameObjects/Components/UserInterface/BoundUserInterface.cs +++ b/Robust.Shared/GameObjects/Components/UserInterface/BoundUserInterface.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using Robust.Shared.IoC; using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Robust.Shared.GameObjects { @@ -16,6 +18,11 @@ namespace Robust.Shared.GameObjects public readonly Enum UiKey; public EntityUid Owner { get; } + /// + /// Additional controls to be disposed when this BUI is disposed. + /// + internal List? Disposals; + /// /// The last received state object sent from the server. /// @@ -45,6 +52,14 @@ namespace Robust.Shared.GameObjects { } + /// + /// Helper method that gets called upon prototype reload. + /// + public virtual void OnProtoReload(PrototypesReloadedEventArgs args) + { + + } + /// /// Invoked when the server sends an arbitrary message. /// @@ -86,6 +101,18 @@ namespace Robust.Shared.GameObjects protected virtual void Dispose(bool disposing) { + if (disposing) + { + if (Disposals != null) + { + foreach (var control in Disposals) + { + control.Dispose(); + } + + Disposals = null; + } + } } } } diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index b012c66df..5254a08a8 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -9,6 +9,7 @@ using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Network; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Reflection; using Robust.Shared.Threading; using Robust.Shared.Timing; @@ -18,18 +19,20 @@ namespace Robust.Shared.GameObjects; public abstract class SharedUserInterfaceSystem : EntitySystem { - [Dependency] private readonly IDynamicTypeFactory _factory = default!; - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly INetManager _netManager = default!; - [Dependency] private readonly IParallelManager _parallel = default!; - [Dependency] private readonly IReflectionManager _reflection = default!; + [Dependency] private readonly IDynamicTypeFactory _factory = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _netManager = default!; + [Dependency] private readonly IParallelManager _parallel = default!; [Dependency] private readonly ISharedPlayerManager _player = default!; - [Dependency] private readonly SharedTransformSystem _transforms = default!; + [Dependency] protected readonly IPrototypeManager ProtoManager = default!; + [Dependency] private readonly IReflectionManager _reflection = default!; + [Dependency] protected readonly ISharedPlayerManager Player = default!; + [Dependency] private readonly SharedTransformSystem _transforms = default!; private EntityQuery _ignoreUIRangeQuery; private EntityQuery _xformQuery; - private EntityQuery _uiQuery; - private EntityQuery _userQuery; + protected EntityQuery UIQuery; + protected EntityQuery UserQuery; private ActorRangeCheckJob _rangeJob; @@ -39,8 +42,8 @@ public abstract class SharedUserInterfaceSystem : EntitySystem _ignoreUIRangeQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); - _uiQuery = GetEntityQuery(); - _userQuery = GetEntityQuery(); + UIQuery = GetEntityQuery(); + UserQuery = GetEntityQuery(); _rangeJob = new() { @@ -78,7 +81,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem var uid = GetEntity(msg.Entity); - if (!_uiQuery.TryComp(uid, out var uiComp)) + if (!UIQuery.TryComp(uid, out var uiComp)) { return; } @@ -134,13 +137,13 @@ public abstract class SharedUserInterfaceSystem : EntitySystem private void OnPlayerAttached(PlayerAttachedEvent ev) { - if (!_userQuery.TryGetComponent(ev.Entity, out var actor)) + if (!UserQuery.TryGetComponent(ev.Entity, out var actor)) return; // Open BUIs upon attachment foreach (var (uid, keys) in actor.OpenInterfaces) { - if (!_uiQuery.TryGetComponent(uid, out var uiComp)) + if (!UIQuery.TryGetComponent(uid, out var uiComp)) continue; // Player can now receive information about open UIs @@ -158,13 +161,13 @@ public abstract class SharedUserInterfaceSystem : EntitySystem private void OnPlayerDetached(PlayerDetachedEvent ev) { - if (!_userQuery.TryGetComponent(ev.Entity, out var actor)) + if (!UserQuery.TryGetComponent(ev.Entity, out var actor)) return; // Close BUIs open detachment. foreach (var (uid, keys) in actor.OpenInterfaces) { - if (!_uiQuery.TryGetComponent(uid, out var uiComp)) + if (!UIQuery.TryGetComponent(uid, out var uiComp)) continue; // Player can no longer receive information about open UIs @@ -187,7 +190,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem private void CloseUiInternal(Entity ent, Enum key, EntityUid actor) { - if (!_uiQuery.Resolve(ent.Owner, ref ent.Comp, false)) + if (!UIQuery.Resolve(ent.Owner, ref ent.Comp, false)) return; if (!ent.Comp.Actors.TryGetValue(key, out var actors)) @@ -201,7 +204,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem // If the actor is also deleting then don't worry about updating what they have open. if (!TerminatingOrDeleted(actor) - && _userQuery.TryComp(actor, out var actorComp) + && UserQuery.TryComp(actor, out var actorComp) && actorComp.OpenInterfaces.TryGetValue(ent.Owner, out var keys)) { keys.Remove(key); @@ -233,7 +236,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem private void OpenUiInternal(Entity ent, Enum key, EntityUid actor) { - if (!_uiQuery.Resolve(ent.Owner, ref ent.Comp, false)) + if (!UIQuery.Resolve(ent.Owner, ref ent.Comp, false)) return; // Similar to the close method this handles actually opening a UI, it just gets relayed here @@ -414,7 +417,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem private void EnsureClientBui(Entity entity, Enum key, InterfaceData data, bool open = true) { // If it's out BUI open it up and apply the state, otherwise do nothing. - var player = _player.LocalEntity; + var player = Player.LocalEntity; if (player == null || !entity.Comp.Actors.TryGetValue(key, out var actors) || @@ -468,7 +471,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public IEnumerable<(EntityUid Entity, Enum Key)> GetActorUis(Entity entity) { - if (!_userQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UserQuery.Resolve(entity.Owner, ref entity.Comp, false)) yield break; foreach (var berry in entity.Comp.OpenInterfaces) @@ -485,7 +488,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public IEnumerable GetActors(Entity entity, Enum key) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false) || !entity.Comp.Actors.TryGetValue(key, out var actors)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false) || !entity.Comp.Actors.TryGetValue(key, out var actors)) yield break; foreach (var actorUid in actors) @@ -499,7 +502,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public void CloseUi(Entity entity, Enum key) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; if (!entity.Comp.Actors.TryGetValue(key, out var actorSet)) @@ -533,7 +536,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem if (actor == null) return; - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; // Short-circuit if no UI. @@ -561,7 +564,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public bool TryOpenUi(Entity entity, Enum key, EntityUid actor, bool predicted = false) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return false; OpenUi(entity, key, actor, predicted); @@ -578,7 +581,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem public void OpenUi(Entity entity, Enum key, EntityUid? actor, bool predicted = false) { - if (actor == null || !_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (actor == null || !UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; // No implementation for that UI key on this ent so short-circuit. @@ -618,7 +621,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public void SetUiState(Entity entity, Enum key, BoundUserInterfaceState? state) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; if (!entity.Comp.Interfaces.ContainsKey(key)) @@ -662,7 +665,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public bool IsUiOpen(Entity entity, Enum uiKey) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return false; if (!entity.Comp.Actors.TryGetValue(uiKey, out var actors)) @@ -674,7 +677,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem public bool IsUiOpen(Entity entity, Enum uiKey, EntityUid actor) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return false; if (!entity.Comp.Actors.TryGetValue(uiKey, out var actors)) @@ -689,7 +692,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem [PublicAPI] public void RaiseUiMessage(Entity entity, Enum key, BoundUserInterfaceMessage message) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; if (!entity.Comp.Actors.TryGetValue(key, out var actors)) @@ -705,7 +708,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public void ServerSendUiMessage(Entity entity, Enum key, BoundUserInterfaceMessage message) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; if (!entity.Comp.Actors.TryGetValue(key, out var actors)) @@ -720,7 +723,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public void ServerSendUiMessage(Entity entity, Enum key, BoundUserInterfaceMessage message, EntityUid actor) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; if (!entity.Comp.Actors.TryGetValue(key, out var actors) || !actors.Contains(actor)) @@ -737,7 +740,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem if (!_netManager.IsClient) return; - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false) || actor.AttachedEntity is not { } attachedEntity) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false) || actor.AttachedEntity is not { } attachedEntity) return; if (!entity.Comp.Actors.TryGetValue(key, out var actors) || !actors.Contains(attachedEntity)) @@ -753,11 +756,11 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public void ClientSendUiMessage(Entity entity, Enum key, BoundUserInterfaceMessage message) { - var player = _player.LocalEntity; + var player = Player.LocalEntity; // Don't send it if we're not a valid actor for it just in case. if (player == null || - !_uiQuery.Resolve(entity.Owner, ref entity.Comp, false) || + !UIQuery.Resolve(entity.Owner, ref entity.Comp, false) || !entity.Comp.Actors.TryGetValue(key, out var actors) || !actors.Contains(player.Value)) { @@ -772,7 +775,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public void CloseUserUis(Entity actor) { - if (!_userQuery.Resolve(actor.Owner, ref actor.Comp, false)) + if (!UserQuery.Resolve(actor.Owner, ref actor.Comp, false)) return; if (actor.Comp.OpenInterfaces.Count == 0) @@ -796,7 +799,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public void CloseUis(Entity entity) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; var toClose = new ValueList(); @@ -816,7 +819,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public void CloseUis(Entity entity, EntityUid actor) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; foreach (var key in entity.Comp.Interfaces.Keys) @@ -830,7 +833,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public void CloseUis(Entity entity, ICommonSession actor) { - if (actor.AttachedEntity is not { } attachedEnt || !_uiQuery.Resolve(entity.Owner, ref entity.Comp, false)) + if (actor.AttachedEntity is not { } attachedEnt || !UIQuery.Resolve(entity.Owner, ref entity.Comp, false)) return; CloseUis(entity, attachedEnt); @@ -843,7 +846,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem { bui = null; - return _uiQuery.Resolve(entity.Owner, ref entity.Comp, false) && entity.Comp.ClientOpenInterfaces.TryGetValue(uiKey, out bui); + return UIQuery.Resolve(entity.Owner, ref entity.Comp, false) && entity.Comp.ClientOpenInterfaces.TryGetValue(uiKey, out bui); } /// @@ -851,7 +854,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public bool TryGetOpenUi(Entity entity, Enum uiKey, [NotNullWhen(true)] out T? bui) where T : BoundUserInterface { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false) || !entity.Comp.ClientOpenInterfaces.TryGetValue(uiKey, out var cBui)) + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false) || !entity.Comp.ClientOpenInterfaces.TryGetValue(uiKey, out var cBui)) { bui = null; return false; @@ -874,7 +877,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem /// public bool TryToggleUi(Entity entity, Enum uiKey, EntityUid actor) { - if (!_uiQuery.Resolve(entity.Owner, ref entity.Comp, false) || + if (!UIQuery.Resolve(entity.Owner, ref entity.Comp, false) || !entity.Comp.Interfaces.ContainsKey(uiKey)) { return false; @@ -954,7 +957,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem var actor = data.Actor; var key = data.Key; - if (data.Result || Deleted(uid) || Deleted(actor) || !_uiQuery.TryComp(uid, out var uiComp)) + if (data.Result || Deleted(uid) || Deleted(actor) || !UIQuery.TryComp(uid, out var uiComp)) continue; CloseUi((uid, uiComp), key, actor);