]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Predict helmet toggles (#22089)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Wed, 6 Dec 2023 06:59:31 +0000 (17:59 +1100)
committerGitHub <noreply@github.com>
Wed, 6 Dec 2023 06:59:31 +0000 (17:59 +1100)
Content.Shared/Clothing/Components/AttachedClothingComponent.cs
Content.Shared/Clothing/Components/ToggleableClothingComponent.cs
Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs
Content.Shared/Inventory/InventorySystem.Equip.cs

index 2e3b965f9728c3446c17f5d4e21dfb8451ab2e51..c52c875952a81d00863e85f4c2536ea43773b1d8 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Clothing.EntitySystems;
+using Robust.Shared.GameStates;
 
 namespace Content.Shared.Clothing.Components;
 
@@ -9,12 +10,12 @@ namespace Content.Shared.Clothing.Components;
 ///     hardsuit helmets.
 /// </summary>
 [Access(typeof(ToggleableClothingSystem))]
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
 public sealed partial class AttachedClothingComponent : Component
 {
     /// <summary>
     ///     The Id of the piece of clothing that this entity belongs to.
     /// </summary>
-    [DataField("AttachedUid")]
-    public EntityUid AttachedUid = default!;
+    [DataField, AutoNetworkedField]
+    public EntityUid AttachedUid;
 }
index b87cd3fee5d8db493b58ced586a9ee6a1637476a..3053efe89aa0ceda0787f81acb44b2afd0b9c799 100644 (file)
@@ -1,17 +1,17 @@
 using Content.Shared.Clothing.EntitySystems;
 using Content.Shared.Inventory;
 using Robust.Shared.Containers;
+using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 namespace Content.Shared.Clothing.Components;
 
 /// <summary>
-///     This component gives an item an action that will equip or un-equip some clothing. Intended for use with
-///     hardsuits and hardsuit helmets.
+///     This component gives an item an action that will equip or un-equip some clothing e.g. hardsuits and hardsuit helmets.
 /// </summary>
 [Access(typeof(ToggleableClothingSystem))]
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
 public sealed partial class ToggleableClothingComponent : Component
 {
     public const string DefaultClothingContainerId = "toggleable-clothing";
@@ -19,35 +19,35 @@ public sealed partial class ToggleableClothingComponent : Component
     /// <summary>
     ///     Action used to toggle the clothing on or off.
     /// </summary>
-    [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
-    public string Action = "ActionToggleSuitPiece";
+    [DataField, AutoNetworkedField]
+    public EntProtoId Action = "ActionToggleSuitPiece";
 
-    [DataField("actionEntity")]
+    [DataField, AutoNetworkedField]
     public EntityUid? ActionEntity;
 
     /// <summary>
     ///     Default clothing entity prototype to spawn into the clothing container.
     /// </summary>
-    [DataField("clothingPrototype", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
-    public string ClothingPrototype = default!;
+    [DataField(required: true), AutoNetworkedField]
+    public EntProtoId ClothingPrototype = default!;
 
     /// <summary>
     ///     The inventory slot that the clothing is equipped to.
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("slot")]
+    [DataField, AutoNetworkedField]
     public string Slot = "head";
 
     /// <summary>
     ///     The inventory slot flags required for this component to function.
     /// </summary>
-    [DataField("requiredSlot")]
+    [DataField("requiredSlot"), AutoNetworkedField]
     public SlotFlags RequiredFlags = SlotFlags.OUTERCLOTHING;
 
     /// <summary>
     ///     The container that the clothing is stored in when not equipped.
     /// </summary>
-    [DataField("containerId")]
+    [DataField, AutoNetworkedField]
     public string ContainerId = DefaultClothingContainerId;
 
     [ViewVariables]
@@ -57,18 +57,18 @@ public sealed partial class ToggleableClothingComponent : Component
     ///     The Id of the piece of clothing that belongs to this component. Required for map-saving if the clothing is
     ///     currently not inside of the container.
     /// </summary>
-    [DataField("clothingUid")]
+    [DataField, AutoNetworkedField]
     public EntityUid? ClothingUid;
 
     /// <summary>
     ///     Time it takes for this clothing to be toggled via the stripping menu verbs. Null prevents the verb from even showing up.
     /// </summary>
-    [DataField("stripDelay")]
+    [DataField, AutoNetworkedField]
     public TimeSpan? StripDelay = TimeSpan.FromSeconds(3);
 
     /// <summary>
     ///     Text shown in the toggle-clothing verb. Defaults to using the name of the <see cref="ActionEntity"/> action.
     /// </summary>
-    [DataField("verbText")]
+    [DataField, AutoNetworkedField]
     public string? VerbText;
 }
index ba006abfdad9cd6501b694fa0464bdb30410a6e0..fa8c1af20854d1aee4e674050cf811297a658f70 100644 (file)
@@ -9,13 +9,17 @@ using Content.Shared.Popups;
 using Content.Shared.Strip;
 using Content.Shared.Verbs;
 using Robust.Shared.Containers;
+using Robust.Shared.Network;
 using Robust.Shared.Serialization;
+using Robust.Shared.Timing;
 using Robust.Shared.Utility;
 
 namespace Content.Shared.Clothing.EntitySystems;
 
 public sealed class ToggleableClothingSystem : EntitySystem
 {
+    [Dependency] private readonly IGameTiming _timing = default!;
+    [Dependency] private readonly INetManager _netMan = default!;
     [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
     [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
     [Dependency] private readonly ActionContainerSystem _actionContainer = default!;
@@ -24,8 +28,6 @@ public sealed class ToggleableClothingSystem : EntitySystem
     [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
     [Dependency] private readonly SharedStrippableSystem _strippable = default!;
 
-    private Queue<EntityUid> _toInsert = new();
-
     public override void Initialize()
     {
         base.Initialize();
@@ -128,18 +130,6 @@ public sealed class ToggleableClothingSystem : EntitySystem
         ToggleClothing(args.User, uid, component);
     }
 
-    public override void Update(float frameTime)
-    {
-        base.Update(frameTime);
-
-        // process delayed insertions. Avoids doing a container insert during a container removal.
-        while (_toInsert.TryDequeue(out var uid))
-        {
-            if (TryComp(uid, out ToggleableClothingComponent? component) && component.ClothingUid != null)
-                component.Container?.Insert(component.ClothingUid.Value);
-        }
-    }
-
     private void OnInteractHand(EntityUid uid, AttachedClothingComponent component, InteractHandEvent args)
     {
         if (args.Handled)
@@ -182,7 +172,7 @@ public sealed class ToggleableClothingSystem : EntitySystem
             _actionsSystem.RemoveAction(action.AttachedEntity.Value, component.ActionEntity);
         }
 
-        if (component.ClothingUid != null)
+        if (component.ClothingUid != null && !_netMan.IsClient)
             QueueDel(component.ClothingUid.Value);
     }
 
@@ -214,6 +204,10 @@ public sealed class ToggleableClothingSystem : EntitySystem
     /// </summary>
     private void OnAttachedUnequip(EntityUid uid, AttachedClothingComponent component, GotUnequippedEvent args)
     {
+        // Let containers worry about it.
+        if (_timing.ApplyingState)
+            return;
+
         if (component.LifeStage > ComponentLifeStage.Running)
             return;
 
@@ -225,7 +219,8 @@ public sealed class ToggleableClothingSystem : EntitySystem
 
         // As unequipped gets called in the middle of container removal, we cannot call a container-insert without causing issues.
         // So we delay it and process it during a system update:
-        _toInsert.Enqueue(component.AttachedUid);
+        if (toggleComp.ClothingUid != null)
+            toggleComp.Container?.Insert(toggleComp.ClothingUid.Value);
     }
 
     /// <summary>
@@ -250,7 +245,7 @@ public sealed class ToggleableClothingSystem : EntitySystem
             _inventorySystem.TryUnequip(user, parent, component.Slot);
         else if (_inventorySystem.TryGetSlotEntity(parent, component.Slot, out var existing))
         {
-            _popupSystem.PopupEntity(Loc.GetString("toggleable-clothing-remove-first", ("entity", existing)),
+            _popupSystem.PopupClient(Loc.GetString("toggleable-clothing-remove-first", ("entity", existing)),
                 user, user);
         }
         else
@@ -294,8 +289,11 @@ public sealed class ToggleableClothingSystem : EntitySystem
         {
             var xform = Transform(uid);
             component.ClothingUid = Spawn(component.ClothingPrototype, xform.Coordinates);
-            EnsureComp<AttachedClothingComponent>(component.ClothingUid.Value).AttachedUid = uid;
+            var attachedClothing = EnsureComp<AttachedClothingComponent>(component.ClothingUid.Value);
+            attachedClothing.AttachedUid = uid;
+            Dirty(component.ClothingUid.Value, attachedClothing);
             component.Container.Insert(component.ClothingUid.Value, EntityManager, ownerTransform: xform);
+            Dirty(uid, component);
         }
 
         if (_actionContainer.EnsureAction(uid, ref component.ActionEntity, out var action, component.Action))
index 44d9c5de3ed8e92a8612d216ce0f58a9580b3625..70083fbfebaa3ac880ad049009770cb2f48fa2d4 100644 (file)
@@ -198,21 +198,8 @@ public abstract partial class InventorySystem
             return false;
         }
 
-        if(!silent && clothing != null && clothing.EquipSound != null && _gameTiming.IsFirstTimePredicted)
+        if (!silent && clothing != null && clothing.EquipSound != null)
         {
-            Filter filter;
-
-            if (_netMan.IsClient)
-                filter = Filter.Local();
-            else
-            {
-                filter = Filter.Pvs(target);
-
-                // don't play double audio for predicted interactions
-                if (predicted)
-                    filter.RemoveWhereAttachedEntity(entity => entity == actor);
-            }
-
             _audio.PlayPredicted(clothing.EquipSound, target, actor);
         }
 
@@ -385,23 +372,13 @@ public abstract partial class InventorySystem
         if (!slotContainer.Remove(removedItem.Value, force: force))
             return false;
 
-        _transform.DropNextTo(removedItem.Value, target);
+        // TODO: Inventory needs a hot cleanup hoo boy
+        // Check if something else (AKA toggleable) dumped it into a container.
+        if (!_containerSystem.IsEntityInContainer(removedItem.Value))
+            _transform.DropNextTo(removedItem.Value, target);
 
-        if (!silent && Resolve(removedItem.Value, ref clothing, false) && clothing.UnequipSound != null && _gameTiming.IsFirstTimePredicted)
+        if (!silent && Resolve(removedItem.Value, ref clothing, false) && clothing.UnequipSound != null)
         {
-            Filter filter;
-
-            if (_netMan.IsClient)
-                filter = Filter.Local();
-            else
-            {
-                filter = Filter.Pvs(target);
-
-                // don't play double audio for predicted interactions
-                if (predicted)
-                    filter.RemoveWhereAttachedEntity(entity => entity == actor);
-            }
-
             _audio.PlayPredicted(clothing.UnequipSound, target, actor);
         }