Fix typing indicators! (#29492)

* First commit

* Removed pause stuff

* Make the event better

* Forgot to add the comment

* Proto id stuff

* cool comments

* serializer

* Added the time stuff
This commit is contained in:
beck-thompson
2024-07-09 22:51:47 -07:00
committed by GitHub
parent 56fb8c832e
commit 91740ef5be
6 changed files with 111 additions and 37 deletions

View File

@@ -2,21 +2,36 @@
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Content.Shared.Inventory;
namespace Content.Client.Chat.TypingIndicator; namespace Content.Client.Chat.TypingIndicator;
public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingIndicatorComponent> public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingIndicatorComponent>
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorComponent component, ref AppearanceChangeEvent args) protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorComponent component, ref AppearanceChangeEvent args)
{ {
if (args.Sprite == null) if (args.Sprite == null)
return; return;
if (!_prototypeManager.TryIndex<TypingIndicatorPrototype>(component.Prototype, out var proto)) var currentTypingIndicator = component.TypingIndicatorPrototype;
var evt = new BeforeShowTypingIndicatorEvent();
if (TryComp<InventoryComponent>(uid, out var inventoryComp))
_inventory.RelayEvent((uid, inventoryComp), ref evt);
var overrideIndicator = evt.GetMostRecentIndicator();
if (overrideIndicator != null)
currentTypingIndicator = overrideIndicator.Value;
if (!_prototypeManager.TryIndex(currentTypingIndicator, out var proto))
{ {
Log.Error($"Unknown typing indicator id: {component.Prototype}"); Log.Error($"Unknown typing indicator id: {component.TypingIndicatorPrototype}");
return; return;
} }

View File

@@ -1,6 +1,8 @@
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Clothing; using Content.Shared.Clothing;
using Content.Shared.Inventory;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Shared.Chat.TypingIndicator; namespace Content.Shared.Chat.TypingIndicator;
@@ -11,6 +13,7 @@ public abstract class SharedTypingIndicatorSystem : EntitySystem
{ {
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly IGameTiming _timing = default!;
/// <summary> /// <summary>
/// Default ID of <see cref="TypingIndicatorPrototype"/> /// Default ID of <see cref="TypingIndicatorPrototype"/>
@@ -26,6 +29,7 @@ public abstract class SharedTypingIndicatorSystem : EntitySystem
SubscribeLocalEvent<TypingIndicatorClothingComponent, ClothingGotEquippedEvent>(OnGotEquipped); SubscribeLocalEvent<TypingIndicatorClothingComponent, ClothingGotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<TypingIndicatorClothingComponent, ClothingGotUnequippedEvent>(OnGotUnequipped); SubscribeLocalEvent<TypingIndicatorClothingComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
SubscribeLocalEvent<TypingIndicatorClothingComponent, InventoryRelayedEvent<BeforeShowTypingIndicatorEvent>>(BeforeShow);
SubscribeAllEvent<TypingChangedEvent>(OnTypingChanged); SubscribeAllEvent<TypingChangedEvent>(OnTypingChanged);
} }
@@ -44,20 +48,19 @@ public abstract class SharedTypingIndicatorSystem : EntitySystem
SetTypingIndicatorEnabled(uid, false); SetTypingIndicatorEnabled(uid, false);
} }
private void OnGotEquipped(EntityUid uid, TypingIndicatorClothingComponent component, ClothingGotEquippedEvent args) private void OnGotEquipped(Entity<TypingIndicatorClothingComponent> entity, ref ClothingGotEquippedEvent args)
{ {
if (!TryComp<TypingIndicatorComponent>(args.Wearer, out var indicator)) entity.Comp.GotEquippedTime = _timing.CurTime;
return;
indicator.Prototype = component.Prototype;
} }
private void OnGotUnequipped(EntityUid uid, TypingIndicatorClothingComponent component, ClothingGotUnequippedEvent args) private void OnGotUnequipped(Entity<TypingIndicatorClothingComponent> entity, ref ClothingGotUnequippedEvent args)
{ {
if (!TryComp<TypingIndicatorComponent>(args.Wearer, out var indicator)) entity.Comp.GotEquippedTime = null;
return; }
indicator.Prototype = InitialIndicatorId; private void BeforeShow(Entity<TypingIndicatorClothingComponent> entity, ref InventoryRelayedEvent<BeforeShowTypingIndicatorEvent> args)
{
args.Args.TryUpdateTimeAndIndicator(entity.Comp.TypingIndicatorPrototype, entity.Comp.GotEquippedTime);
} }
private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args) private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args)

View File

@@ -1,18 +0,0 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Chat.TypingIndicator;
/// <summary>
/// Networked event from client.
/// Send to server when client started/stopped typing in chat input field.
/// </summary>
[Serializable, NetSerializable]
public sealed class TypingChangedEvent : EntityEventArgs
{
public readonly bool IsTyping;
public TypingChangedEvent(bool isTyping)
{
IsTyping = isTyping;
}
}

View File

@@ -1,13 +1,28 @@
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Chat.TypingIndicator; namespace Content.Shared.Chat.TypingIndicator;
[RegisterComponent, NetworkedComponent] /// <summary>
/// If an item is equipped to someones inventory (Anything but the pockets), and has this component
/// the users typing indicator will be replaced by the prototype given in <c>TypingIndicatorPrototype</c>.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
[Access(typeof(SharedTypingIndicatorSystem))] [Access(typeof(SharedTypingIndicatorSystem))]
public sealed partial class TypingIndicatorClothingComponent : Component public sealed partial class TypingIndicatorClothingComponent : Component
{ {
/// <summary>
/// The typing indicator that will override the default typing indicator when the item is equipped to a users
/// inventory.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("proto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<TypingIndicatorPrototype>))] [DataField("proto", required: true)]
public string Prototype = default!; public ProtoId<TypingIndicatorPrototype> TypingIndicatorPrototype = default!;
/// <summary>
/// This stores the time the item was equipped in someones inventory. If null, item is currently not equipped.
/// </summary>
[DataField, AutoPausedField]
public TimeSpan? GotEquippedTime = null;
} }

View File

@@ -1,5 +1,5 @@
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Prototypes;
namespace Content.Shared.Chat.TypingIndicator; namespace Content.Shared.Chat.TypingIndicator;
@@ -14,7 +14,6 @@ public sealed partial class TypingIndicatorComponent : Component
/// <summary> /// <summary>
/// Prototype id that store all visual info about typing indicator. /// Prototype id that store all visual info about typing indicator.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField("proto")]
[DataField("proto", customTypeSerializer: typeof(PrototypeIdSerializer<TypingIndicatorPrototype>))] public ProtoId<TypingIndicatorPrototype> TypingIndicatorPrototype = "default";
public string Prototype = SharedTypingIndicatorSystem.InitialIndicatorId;
} }

View File

@@ -0,0 +1,60 @@
using Robust.Shared.Serialization;
using Content.Shared.Inventory;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Exceptions;
namespace Content.Shared.Chat.TypingIndicator;
/// <summary>
/// Networked event from client.
/// Send to server when client started/stopped typing in chat input field.
/// </summary>
[Serializable, NetSerializable]
public sealed class TypingChangedEvent : EntityEventArgs
{
public readonly bool IsTyping;
public TypingChangedEvent(bool isTyping)
{
IsTyping = isTyping;
}
}
/// <summary>
/// This event will be broadcast right before displaying an entities typing indicator.
/// If _overrideIndicator is not null after the event is finished it will be used.
/// </summary>
[Serializable, NetSerializable]
public sealed class BeforeShowTypingIndicatorEvent : IInventoryRelayEvent
{
public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
private ProtoId<TypingIndicatorPrototype>? _overrideIndicator = null;
private TimeSpan? _latestEquipTime = null;
public BeforeShowTypingIndicatorEvent()
{
_overrideIndicator = null;
_latestEquipTime = null;
}
/// <summary>
/// Will only update the time and indicator if the given time is more recent than
/// the stored time or if the stored time is null.
/// </summary>
/// <returns>
/// True if the given time is more recent than the stored time, and false otherwise.
/// </returns>
public bool TryUpdateTimeAndIndicator(ProtoId<TypingIndicatorPrototype>? indicator, TimeSpan? equipTime)
{
if (equipTime != null && (_latestEquipTime == null || _latestEquipTime < equipTime))
{
_latestEquipTime = equipTime;
_overrideIndicator = indicator;
return true;
}
return false;
}
public ProtoId<TypingIndicatorPrototype>? GetMostRecentIndicator()
{
return _overrideIndicator;
}
}