Fix handlabeler/utility belt misprediction (#26660)
* 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>
This commit is contained in:
18
Content.Client/Labels/EntitySystems/HandLabelerSystem.cs
Normal file
18
Content.Client/Labels/EntitySystems/HandLabelerSystem.cs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the UI state based on server-sent info
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (_window == null || state is not HandLabelerBoundUserInterfaceState cast)
|
||||
// Focus moment
|
||||
if (_entManager.TryGetComponent(Owner, out HandLabelerComponent? labeler) &&
|
||||
labeler.AssignedLabel.Equals(newLabel))
|
||||
return;
|
||||
|
||||
_window.SetCurrentLabel(cast.CurrentLabel);
|
||||
SendPredictedMessage(new HandLabelerLabelChangedMessage(newLabel));
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
if (_window == null || !_entManager.TryGetComponent(Owner, out HandLabelerComponent? component))
|
||||
return;
|
||||
|
||||
_window.SetCurrentLabel(component.AssignedLabel);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -9,16 +9,39 @@ 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)
|
||||
{
|
||||
if (label == _label)
|
||||
return;
|
||||
|
||||
_label = label;
|
||||
if (!_focused)
|
||||
LabelLineEdit.Text = label;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
namespace Content.Server.Labels.Label;
|
||||
|
||||
public sealed class HandLabelerSystem : SharedHandLabelerSystem
|
||||
{
|
||||
/// <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);
|
||||
|
||||
// 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;
|
||||
|
||||
_userInterfaceSystem.SetUiState(uid, HandLabelerUiKey.Key,
|
||||
new HandLabelerBoundUserInterfaceState(handLabeler.AssignedLabel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
30
Content.Shared/Labels/Components/HandLabelerComponent.cs
Normal file
30
Content.Shared/Labels/Components/HandLabelerComponent.cs
Normal file
@@ -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;
|
||||
}
|
||||
129
Content.Shared/Labels/EntitySystems/SharedHandLabelerSystem.cs
Normal file
129
Content.Shared/Labels/EntitySystems/SharedHandLabelerSystem.cs
Normal file
@@ -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}\"");
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Labels
|
||||
{
|
||||
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.
|
||||
@@ -20,28 +20,8 @@ namespace Content.Shared.Labels
|
||||
LabelType
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="HandLabelerComponent"/> state that can be sent to the client
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class HandLabelerBoundUserInterfaceState : BoundUserInterfaceState
|
||||
public sealed class HandLabelerLabelChangedMessage(string label) : BoundUserInterfaceMessage
|
||||
{
|
||||
public string CurrentLabel { get; }
|
||||
|
||||
public HandLabelerBoundUserInterfaceState(string currentLabel)
|
||||
{
|
||||
CurrentLabel = currentLabel;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class HandLabelerLabelChangedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public string Label { get; }
|
||||
|
||||
public HandLabelerLabelChangedMessage(string label)
|
||||
{
|
||||
Label = label;
|
||||
}
|
||||
}
|
||||
public string Label { get; } = label;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user