]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fix handlabeler/utility belt misprediction (#26660)
authorosjarw <62134478+osjarw@users.noreply.github.com>
Sun, 28 Apr 2024 04:19:30 +0000 (07:19 +0300)
committerGitHub <noreply@github.com>
Sun, 28 Apr 2024 04:19:30 +0000 (14:19 +1000)
* Fix handlabeler/utility belt misprediction

* Partly moved HandLabelerSystem to shared
And cleaned up HandLabelerComponent

* WIP format the files so later commits look clearer
Doesn't change individual code lines, but may move
functions to another file

* WIP some more code movement

* Hand Labeler is now mostly predicted
Only the UI isn't

* WIP: Formatting and moved stuff

* Using componentstates for prediction correction

* review

* Update label on label change

* Don't overwrite label while editing

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
Content.Client/Labels/EntitySystems/HandLabelerSystem.cs [new file with mode: 0644]
Content.Client/Labels/UI/HandLabelerBoundUserInterface.cs
Content.Client/Labels/UI/HandLabelerWindow.xaml.cs
Content.Server/Labels/Label/Components/HandLabelerComponent.cs [deleted file]
Content.Server/Labels/Label/HandLabelerSystem.cs
Content.Server/Labels/Label/LabelSystem.cs
Content.Shared/Labels/Components/HandLabelerComponent.cs [new file with mode: 0644]
Content.Shared/Labels/EntitySystems/SharedHandLabelerSystem.cs [new file with mode: 0644]
Content.Shared/Labels/EntitySystems/SharedLabelSystem.cs
Content.Shared/Labels/LabelEvents.cs

diff --git a/Content.Client/Labels/EntitySystems/HandLabelerSystem.cs b/Content.Client/Labels/EntitySystems/HandLabelerSystem.cs
new file mode 100644 (file)
index 0000000..e956014
--- /dev/null
@@ -0,0 +1,18 @@
+using Content.Client.Labels.UI;
+using Content.Shared.Labels;
+using Content.Shared.Labels.Components;
+using Content.Shared.Labels.EntitySystems;
+
+namespace Content.Client.Labels.EntitySystems;
+
+public sealed class HandLabelerSystem : SharedHandLabelerSystem
+{
+    protected override void UpdateUI(Entity<HandLabelerComponent> ent)
+    {
+        if (UserInterfaceSystem.TryGetOpenUi(ent.Owner, HandLabelerUiKey.Key, out var bui)
+            && bui is HandLabelerBoundUserInterface cBui)
+        {
+            cBui.Reload();
+        }
+    }
+}
index d393c43f9366ea09e78a29e89138860ee40252e8..555f1ff09e697bbeed743761e10dde60f8f634c5 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Labels;
+using Content.Shared.Labels.Components;
 using Robust.Client.GameObjects;
 
 namespace Content.Client.Labels.UI
@@ -8,11 +9,14 @@ namespace Content.Client.Labels.UI
     /// </summary>
     public sealed class HandLabelerBoundUserInterface : BoundUserInterface
     {
+        [Dependency] private readonly IEntityManager _entManager = default!;
+
         [ViewVariables]
         private HandLabelerWindow? _window;
 
         public HandLabelerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
         {
+            IoCManager.InjectDependencies(this);
         }
 
         protected override void Open()
@@ -27,24 +31,25 @@ namespace Content.Client.Labels.UI
 
             _window.OnClose += Close;
             _window.OnLabelChanged += OnLabelChanged;
+            Reload();
         }
 
         private void OnLabelChanged(string newLabel)
         {
-            SendMessage(new HandLabelerLabelChangedMessage(newLabel));
+            // Focus moment
+            if (_entManager.TryGetComponent(Owner, out HandLabelerComponent? labeler) &&
+                labeler.AssignedLabel.Equals(newLabel))
+                return;
+
+            SendPredictedMessage(new HandLabelerLabelChangedMessage(newLabel));
         }
 
-        /// <summary>
-        /// Update the UI state based on server-sent info
-        /// </summary>
-        /// <param name="state"></param>
-        protected override void UpdateState(BoundUserInterfaceState state)
+        public void Reload()
         {
-            base.UpdateState(state);
-            if (_window == null || state is not HandLabelerBoundUserInterfaceState cast)
+            if (_window == null || !_entManager.TryGetComponent(Owner, out HandLabelerComponent? component))
                 return;
 
-            _window.SetCurrentLabel(cast.CurrentLabel);
+            _window.SetCurrentLabel(component.AssignedLabel);
         }
 
         protected override void Dispose(bool disposing)
index 7706c31f85aac87db16c174408d61b5dda0e83a4..6482cdc1cc206f5e9bdee3e2f8ea300492d56350 100644 (file)
@@ -9,17 +9,40 @@ namespace Content.Client.Labels.UI
     {
         public event Action<string>? OnLabelChanged;
 
+        /// <summary>
+        /// Is the user currently entering text into the control?
+        /// </summary>
+        private bool _focused;
+        // TODO LineEdit Make this a bool on the LineEdit control
+
+        private string _label = string.Empty;
+
         public HandLabelerWindow()
         {
             RobustXamlLoader.Load(this);
 
-            LabelLineEdit.OnTextEntered += e => OnLabelChanged?.Invoke(e.Text);
-            LabelLineEdit.OnFocusExit += e => OnLabelChanged?.Invoke(e.Text);
+            LabelLineEdit.OnTextEntered += e =>
+            {
+                _label = e.Text;
+                OnLabelChanged?.Invoke(_label);
+            };
+
+            LabelLineEdit.OnFocusEnter += _ => _focused = true;
+            LabelLineEdit.OnFocusExit += _ =>
+            {
+                _focused = false;
+                LabelLineEdit.Text = _label;
+            };
         }
 
         public void SetCurrentLabel(string label)
         {
-            LabelLineEdit.Text = label;
+            if (label == _label)
+                return;
+
+            _label = label;
+            if (!_focused)
+                LabelLineEdit.Text = label;
         }
     }
 }
diff --git a/Content.Server/Labels/Label/Components/HandLabelerComponent.cs b/Content.Server/Labels/Label/Components/HandLabelerComponent.cs
deleted file mode 100644 (file)
index 6c96cad..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-using Content.Shared.Whitelist;
-
-namespace Content.Server.Labels.Components
-{
-    [RegisterComponent]
-    public sealed partial class HandLabelerComponent : Component
-    {
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("assignedLabel")]
-        public string AssignedLabel { get; set; } = string.Empty;
-
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("maxLabelChars")]
-        public int MaxLabelChars { get; set; } = 50;
-
-        [DataField("whitelist")]
-        public EntityWhitelist Whitelist = new();
-    }
-}
index 84c41b0db57e543b57a21866046d835ad8538321..d52bf260460dbce00a170e17855b5f2c6b632e71 100644 (file)
@@ -1,116 +1,8 @@
-using Content.Server.Labels.Components;
-using Content.Server.UserInterface;
-using Content.Server.Popups;
-using Content.Shared.Administration.Logs;
-using Content.Shared.Database;
-using Content.Shared.Interaction;
-using Content.Shared.Labels;
-using Content.Shared.Verbs;
-using JetBrains.Annotations;
-using Robust.Server.GameObjects;
-using Robust.Shared.Player;
+using Content.Shared.Labels.EntitySystems;
 
-namespace Content.Server.Labels
-{
-    /// <summary>
-    /// A hand labeler system that lets an object apply labels to objects with the <see cref="LabelComponent"/> .
-    /// </summary>
-    [UsedImplicitly]
-    public sealed class HandLabelerSystem : EntitySystem
-    {
-        [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
-        [Dependency] private readonly PopupSystem _popupSystem = default!;
-        [Dependency] private readonly LabelSystem _labelSystem = default!;
-        [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
-
-        public override void Initialize()
-        {
-            base.Initialize();
-
-            SubscribeLocalEvent<HandLabelerComponent, AfterInteractEvent>(AfterInteractOn);
-            SubscribeLocalEvent<HandLabelerComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb);
-            // Bound UI subscriptions
-            SubscribeLocalEvent<HandLabelerComponent, HandLabelerLabelChangedMessage>(OnHandLabelerLabelChanged);
-        }
-
-        private void OnUtilityVerb(EntityUid uid, HandLabelerComponent handLabeler, GetVerbsEvent<UtilityVerb> args)
-        {
-            if (args.Target is not { Valid: true } target || !handLabeler.Whitelist.IsValid(target) || !args.CanAccess)
-                return;
-
-            string labelerText = handLabeler.AssignedLabel == string.Empty ? Loc.GetString("hand-labeler-remove-label-text") : Loc.GetString("hand-labeler-add-label-text");
-
-            var verb = new UtilityVerb()
-            {
-                Act = () =>
-                {
-                    AddLabelTo(uid, handLabeler, target, out var result);
-                    if (result != null)
-                        _popupSystem.PopupEntity(result, args.User, args.User);
-                },
-                Text = labelerText
-            };
-
-            args.Verbs.Add(verb);
-        }
-
-        private void AfterInteractOn(EntityUid uid, HandLabelerComponent handLabeler, AfterInteractEvent args)
-        {
-            if (args.Target is not {Valid: true} target || !handLabeler.Whitelist.IsValid(target) || !args.CanReach)
-                return;
-
-            AddLabelTo(uid, handLabeler, target, out string? result);
-            if (result == null)
-                return;
-            _popupSystem.PopupEntity(result, args.User, args.User);
+namespace Content.Server.Labels.Label;
 
-            // Log labeling
-            _adminLogger.Add(LogType.Action, LogImpact.Low,
-                $"{ToPrettyString(args.User):user} labeled {ToPrettyString(target):target} with {ToPrettyString(uid):labeler}");
-        }
-
-        private void AddLabelTo(EntityUid uid, HandLabelerComponent? handLabeler, EntityUid target, out string? result)
-        {
-            if (!Resolve(uid, ref handLabeler))
-            {
-                result = null;
-                return;
-            }
-
-            if (handLabeler.AssignedLabel == string.Empty)
-            {
-                _labelSystem.Label(target, null);
-                result = Loc.GetString("hand-labeler-successfully-removed");
-                return;
-            }
-
-            _labelSystem.Label(target, handLabeler.AssignedLabel);
-            result = Loc.GetString("hand-labeler-successfully-applied");
-        }
-
-        private void OnHandLabelerLabelChanged(EntityUid uid, HandLabelerComponent handLabeler, HandLabelerLabelChangedMessage args)
-        {
-            if (args.Actor is not {Valid: true} player)
-                return;
-
-            var label = args.Label.Trim();
-            handLabeler.AssignedLabel = label.Substring(0, Math.Min(handLabeler.MaxLabelChars, label.Length));
-            DirtyUI(uid, handLabeler);
-
-            // Log label change
-            _adminLogger.Add(LogType.Action, LogImpact.Low,
-                $"{ToPrettyString(player):user} set {ToPrettyString(uid):labeler} to apply label \"{handLabeler.AssignedLabel}\"");
-
-        }
-
-        private void DirtyUI(EntityUid uid,
-            HandLabelerComponent? handLabeler = null)
-        {
-            if (!Resolve(uid, ref handLabeler))
-                return;
+public sealed class HandLabelerSystem : SharedHandLabelerSystem
+{
 
-            _userInterfaceSystem.SetUiState(uid, HandLabelerUiKey.Key,
-                new HandLabelerBoundUserInterfaceState(handLabeler.AssignedLabel));
-        }
-    }
 }
index dd4ba1f718f3938bc2f779ebde6c0c92c8ca1e79..aee2abe7ab9f7dba0dc23d022cd7a5a582a6986d 100644 (file)
@@ -50,7 +50,7 @@ namespace Content.Server.Labels
         /// <param name="text">intended label text (null to remove)</param>
         /// <param name="label">label component for resolve</param>
         /// <param name="metadata">metadata component for resolve</param>
-        public void Label(EntityUid uid, string? text, MetaDataComponent? metadata = null, LabelComponent? label = null)
+        public override void Label(EntityUid uid, string? text, MetaDataComponent? metadata = null, LabelComponent? label = null)
         {
             if (!Resolve(uid, ref metadata))
                 return;
diff --git a/Content.Shared/Labels/Components/HandLabelerComponent.cs b/Content.Shared/Labels/Components/HandLabelerComponent.cs
new file mode 100644 (file)
index 0000000..8e2cb7b
--- /dev/null
@@ -0,0 +1,30 @@
+using Content.Shared.Labels.EntitySystems;
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Labels.Components;
+
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedHandLabelerSystem))]
+public sealed partial class HandLabelerComponent : Component
+{
+    [ViewVariables(VVAccess.ReadWrite), Access(Other = AccessPermissions.ReadWriteExecute)]
+    [DataField]
+    public string AssignedLabel = string.Empty;
+
+    [ViewVariables(VVAccess.ReadWrite)]
+    [DataField]
+    public int MaxLabelChars = 50;
+
+    [DataField]
+    public EntityWhitelist Whitelist = new();
+}
+
+[Serializable, NetSerializable]
+public sealed class HandLabelerComponentState(string assignedLabel) : IComponentState
+{
+    public string AssignedLabel = assignedLabel;
+
+    public int MaxLabelChars;
+}
diff --git a/Content.Shared/Labels/EntitySystems/SharedHandLabelerSystem.cs b/Content.Shared/Labels/EntitySystems/SharedHandLabelerSystem.cs
new file mode 100644 (file)
index 0000000..7dbeee3
--- /dev/null
@@ -0,0 +1,129 @@
+using Content.Shared.Administration.Logs;
+using Content.Shared.Database;
+using Content.Shared.Interaction;
+using Content.Shared.Labels.Components;
+using Content.Shared.Popups;
+using Content.Shared.Verbs;
+using Robust.Shared.GameStates;
+using Robust.Shared.Network;
+
+namespace Content.Shared.Labels.EntitySystems;
+
+public abstract class SharedHandLabelerSystem : EntitySystem
+{
+    [Dependency] protected readonly SharedUserInterfaceSystem UserInterfaceSystem = default!;
+    [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
+    [Dependency] private readonly SharedLabelSystem _labelSystem = default!;
+    [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+    [Dependency] private readonly INetManager _netManager = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<HandLabelerComponent, AfterInteractEvent>(AfterInteractOn);
+        SubscribeLocalEvent<HandLabelerComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb);
+        // Bound UI subscriptions
+        SubscribeLocalEvent<HandLabelerComponent, HandLabelerLabelChangedMessage>(OnHandLabelerLabelChanged);
+        SubscribeLocalEvent<HandLabelerComponent, ComponentGetState>(OnGetState);
+        SubscribeLocalEvent<HandLabelerComponent, ComponentHandleState>(OnHandleState);
+    }
+
+    private void OnGetState(Entity<HandLabelerComponent> ent, ref ComponentGetState args)
+    {
+        args.State = new HandLabelerComponentState(ent.Comp.AssignedLabel)
+        {
+            MaxLabelChars = ent.Comp.MaxLabelChars,
+        };
+    }
+
+    private void OnHandleState(Entity<HandLabelerComponent> ent, ref ComponentHandleState args)
+    {
+        if (args.Current is not HandLabelerComponentState state)
+            return;
+
+        ent.Comp.MaxLabelChars = state.MaxLabelChars;
+
+        if (ent.Comp.AssignedLabel == state.AssignedLabel)
+            return;
+
+        ent.Comp.AssignedLabel = state.AssignedLabel;
+        UpdateUI(ent);
+    }
+
+    protected virtual void UpdateUI(Entity<HandLabelerComponent> ent)
+    {
+    }
+
+    private void AddLabelTo(EntityUid uid, HandLabelerComponent? handLabeler, EntityUid target, out string? result)
+    {
+        if (!Resolve(uid, ref handLabeler))
+        {
+            result = null;
+            return;
+        }
+
+        if (handLabeler.AssignedLabel == string.Empty)
+        {
+            if (_netManager.IsServer)
+                _labelSystem.Label(target, null);
+            result = Loc.GetString("hand-labeler-successfully-removed");
+            return;
+        }
+        if (_netManager.IsServer)
+            _labelSystem.Label(target, handLabeler.AssignedLabel);
+        result = Loc.GetString("hand-labeler-successfully-applied");
+    }
+
+    private void OnUtilityVerb(EntityUid uid, HandLabelerComponent handLabeler, GetVerbsEvent<UtilityVerb> args)
+    {
+        if (args.Target is not { Valid: true } target || !handLabeler.Whitelist.IsValid(target) || !args.CanAccess)
+            return;
+
+        var labelerText = handLabeler.AssignedLabel == string.Empty ? Loc.GetString("hand-labeler-remove-label-text") : Loc.GetString("hand-labeler-add-label-text");
+
+        var verb = new UtilityVerb()
+        {
+            Act = () =>
+            {
+                Labeling(uid, target, args.User, handLabeler);
+            },
+            Text = labelerText
+        };
+
+        args.Verbs.Add(verb);
+    }
+
+    private void AfterInteractOn(EntityUid uid, HandLabelerComponent handLabeler, AfterInteractEvent args)
+    {
+        if (args.Target is not { Valid: true } target || !handLabeler.Whitelist.IsValid(target) || !args.CanReach)
+            return;
+
+        Labeling(uid, target, args.User, handLabeler);
+    }
+
+    private void Labeling(EntityUid uid, EntityUid target, EntityUid User, HandLabelerComponent handLabeler)
+    {
+        AddLabelTo(uid, handLabeler, target, out var result);
+        if (result == null)
+            return;
+
+        _popupSystem.PopupClient(result, User, User);
+
+        // Log labeling
+        _adminLogger.Add(LogType.Action, LogImpact.Low,
+            $"{ToPrettyString(User):user} labeled {ToPrettyString(target):target} with {ToPrettyString(uid):labeler}");
+    }
+
+    private void OnHandLabelerLabelChanged(EntityUid uid, HandLabelerComponent handLabeler, HandLabelerLabelChangedMessage args)
+    {
+        var label = args.Label.Trim();
+        handLabeler.AssignedLabel = label[..Math.Min(handLabeler.MaxLabelChars, label.Length)];
+        UpdateUI((uid, handLabeler));
+        Dirty(uid, handLabeler);
+
+        // Log label change
+        _adminLogger.Add(LogType.Action, LogImpact.Low,
+            $"{ToPrettyString(args.Actor):user} set {ToPrettyString(uid):labeler} to apply label \"{handLabeler.AssignedLabel}\"");
+    }
+}
index a8239e7867b979b07521d79495fb9a8b6dd5fbeb..1189bb46d043ba3c347c0e441f1a1517ce338ba4 100644 (file)
@@ -13,6 +13,8 @@ public abstract partial class SharedLabelSystem : EntitySystem
         SubscribeLocalEvent<LabelComponent, ExaminedEvent>(OnExamine);
     }
 
+    public virtual void Label(EntityUid uid, string? text, MetaDataComponent? metadata = null, LabelComponent? label = null){}
+
     private void OnExamine(EntityUid uid, LabelComponent? label, ExaminedEvent args)
     {
         if (!Resolve(uid, ref label))
index 9f00354af24c5a902470fd9a8e6b1df814d1311f..62e9c15c85e8d99f10017c3e1882ab23a89c63ce 100644 (file)
@@ -1,47 +1,27 @@
 using Robust.Shared.Serialization;
 
-namespace Content.Shared.Labels
-{
-    /// <summary>
-    /// Key representing which <see cref="PlayerBoundUserInterface"/> is currently open.
-    /// Useful when there are multiple UI for an object. Here it's future-proofing only.
-    /// </summary>
-    [Serializable, NetSerializable]
-    public enum HandLabelerUiKey
-    {
-        Key,
-    }
-
-    [Serializable, NetSerializable]
-    public enum PaperLabelVisuals : byte
-    {
-        Layer,
-        HasLabel,
-        LabelType
-    }
+namespace Content.Shared.Labels;
 
-    /// <summary>
-    /// Represents a <see cref="HandLabelerComponent"/> state that can be sent to the client
-    /// </summary>
-    [Serializable, NetSerializable]
-    public sealed class HandLabelerBoundUserInterfaceState : BoundUserInterfaceState
-    {
-        public string CurrentLabel { get; }
-
-        public HandLabelerBoundUserInterfaceState(string currentLabel)
-        {
-            CurrentLabel = currentLabel;
-        }
-    }
+/// <summary>
+/// Key representing which <see cref="PlayerBoundUserInterface"/> is currently open.
+/// Useful when there are multiple UI for an object. Here it's future-proofing only.
+/// </summary>
+[Serializable, NetSerializable]
+public enum HandLabelerUiKey
+{
+    Key,
+}
 
-    [Serializable, NetSerializable]
-    public sealed class HandLabelerLabelChangedMessage : BoundUserInterfaceMessage
-    {
-        public string Label { get; }
+[Serializable, NetSerializable]
+public enum PaperLabelVisuals : byte
+{
+    Layer,
+    HasLabel,
+    LabelType
+}
 
-        public HandLabelerLabelChangedMessage(string label)
-        {
-            Label = label;
-        }
-    }
+[Serializable, NetSerializable]
+public sealed class HandLabelerLabelChangedMessage(string label) : BoundUserInterfaceMessage
+{
+    public string Label { get; } = label;
 }