]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Holopad networking rework (#34112)
authorchromiumboy <50505512+chromiumboy@users.noreply.github.com>
Fri, 17 Jan 2025 20:09:59 +0000 (14:09 -0600)
committerGitHub <noreply@github.com>
Fri, 17 Jan 2025 20:09:59 +0000 (14:09 -0600)
* Initial commit

* Finalizing main changes

* Addressed reviews

* Fixed a few issues

* Switched to using global overrides

* Removed unnecessary references

Content.Client/Holopad/HolopadSystem.cs
Content.Server/Holopad/HolopadSystem.cs
Content.Shared/Holopad/HolographicAvatarComponent.cs
Content.Shared/Holopad/HolopadHologramComponent.cs
Content.Shared/Holopad/HolopadUserComponent.cs
Resources/Prototypes/Entities/Structures/Machines/holopad.yml

index 3bd556f1fc23292533be47f0d0350059f9e588c7..6aad39fe2473a271250553c3599dc2156bebb70d 100644 (file)
@@ -2,45 +2,38 @@ using Content.Shared.Chat.TypingIndicator;
 using Content.Shared.Holopad;
 using Robust.Client.GameObjects;
 using Robust.Client.Graphics;
-using Robust.Client.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
 using System.Linq;
+using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
 
 namespace Content.Client.Holopad;
 
 public sealed class HolopadSystem : SharedHolopadSystem
 {
     [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-    [Dependency] private readonly IPlayerManager _playerManager = default!;
     [Dependency] private readonly IGameTiming _timing = default!;
 
     public override void Initialize()
     {
         base.Initialize();
 
-        SubscribeLocalEvent<HolopadHologramComponent, ComponentInit>(OnComponentInit);
+        SubscribeLocalEvent<HolopadHologramComponent, ComponentStartup>(OnComponentStartup);
         SubscribeLocalEvent<HolopadHologramComponent, BeforePostShaderRenderEvent>(OnShaderRender);
         SubscribeAllEvent<TypingChangedEvent>(OnTypingChanged);
-
-        SubscribeNetworkEvent<PlayerSpriteStateRequest>(OnPlayerSpriteStateRequest);
-        SubscribeNetworkEvent<PlayerSpriteStateMessage>(OnPlayerSpriteStateMessage);
     }
 
-    private void OnComponentInit(EntityUid uid, HolopadHologramComponent component, ComponentInit ev)
+    private void OnComponentStartup(Entity<HolopadHologramComponent> entity, ref ComponentStartup ev)
     {
-        if (!TryComp<SpriteComponent>(uid, out var sprite))
-            return;
-
-        UpdateHologramSprite(uid);
+        UpdateHologramSprite(entity, entity.Comp.LinkedEntity);
     }
 
-    private void OnShaderRender(EntityUid uid, HolopadHologramComponent component, BeforePostShaderRenderEvent ev)
+    private void OnShaderRender(Entity<HolopadHologramComponent> entity, ref BeforePostShaderRenderEvent ev)
     {
         if (ev.Sprite.PostShader == null)
             return;
 
-        ev.Sprite.PostShader.SetParameter("t", (float)_timing.CurTime.TotalSeconds * component.ScrollRate);
+        UpdateHologramSprite(entity, entity.Comp.LinkedEntity);
     }
 
     private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args)
@@ -57,100 +50,66 @@ public sealed class HolopadSystem : SharedHolopadSystem
         RaiseNetworkEvent(netEv);
     }
 
-    private void OnPlayerSpriteStateRequest(PlayerSpriteStateRequest ev)
+    private void UpdateHologramSprite(EntityUid hologram, EntityUid? target)
     {
-        var targetPlayer = GetEntity(ev.TargetPlayer);
-        var player = _playerManager.LocalSession?.AttachedEntity;
-
-        // Ignore the request if received by a player who isn't the target
-        if (targetPlayer != player)
+        // Get required components
+        if (!TryComp<SpriteComponent>(hologram, out var hologramSprite) ||
+            !TryComp<HolopadHologramComponent>(hologram, out var holopadhologram))
             return;
 
-        if (!TryComp<SpriteComponent>(player, out var playerSprite))
-            return;
-
-        var spriteLayerData = new List<PrototypeLayerData>();
+        // Remove all sprite layers
+        for (int i = hologramSprite.AllLayers.Count() - 1; i >= 0; i--)
+            hologramSprite.RemoveLayer(i);
 
-        if (playerSprite.Visible)
+        if (TryComp<SpriteComponent>(target, out var targetSprite))
         {
-            // Record the RSI paths, state names and shader paramaters of all visible layers
-            for (int i = 0; i < playerSprite.AllLayers.Count(); i++)
+            // Use the target's holographic avatar (if available)
+            if (TryComp<HolographicAvatarComponent>(target, out var targetAvatar) &&
+                targetAvatar.LayerData != null)
             {
-                if (!playerSprite.TryGetLayer(i, out var layer))
-                    continue;
-
-                if (!layer.Visible ||
-                    string.IsNullOrEmpty(layer.ActualRsi?.Path.ToString()) ||
-                    string.IsNullOrEmpty(layer.State.Name))
-                    continue;
-
-                var layerDatum = new PrototypeLayerData();
-                layerDatum.RsiPath = layer.ActualRsi.Path.ToString();
-                layerDatum.State = layer.State.Name;
-
-                if (layer.CopyToShaderParameters != null)
+                for (int i = 0; i < targetAvatar.LayerData.Length; i++)
                 {
-                    var key = (string)layer.CopyToShaderParameters.LayerKey;
-
-                    if (playerSprite.LayerMapTryGet(key, out var otherLayerIdx) &&
-                        playerSprite.TryGetLayer(otherLayerIdx, out var otherLayer) &&
-                        otherLayer.Visible)
-                    {
-                        layerDatum.MapKeys = new() { key };
-
-                        layerDatum.CopyToShaderParameters = new PrototypeCopyToShaderParameters()
-                        {
-                            LayerKey = key,
-                            ParameterTexture = layer.CopyToShaderParameters.ParameterTexture,
-                            ParameterUV = layer.CopyToShaderParameters.ParameterUV
-                        };
-                    }
+                    var layer = targetAvatar.LayerData[i];
+                    hologramSprite.AddLayer(targetAvatar.LayerData[i], i);
                 }
+            }
 
-                spriteLayerData.Add(layerDatum);
+            // Otherwise copy the target's current physical appearance
+            else
+            {
+                hologramSprite.CopyFrom(targetSprite);
             }
         }
 
-        // Return the recorded data to the server
-        var evResponse = new PlayerSpriteStateMessage(ev.TargetPlayer, spriteLayerData.ToArray());
-        RaiseNetworkEvent(evResponse);
-    }
-
-    private void OnPlayerSpriteStateMessage(PlayerSpriteStateMessage ev)
-    {
-        UpdateHologramSprite(GetEntity(ev.SpriteEntity), ev.SpriteLayerData);
-    }
-
-    private void UpdateHologramSprite(EntityUid uid, PrototypeLayerData[]? layerData = null)
-    {
-        if (!TryComp<SpriteComponent>(uid, out var hologramSprite))
-            return;
-
-        if (!TryComp<HolopadHologramComponent>(uid, out var holopadhologram))
-            return;
+        // There is no target, display a default sprite instead (if available)
+        else
+        {
+            if (string.IsNullOrEmpty(holopadhologram.RsiPath) || string.IsNullOrEmpty(holopadhologram.RsiState))
+                return;
 
-        for (int i = hologramSprite.AllLayers.Count() - 1; i >= 0; i--)
-            hologramSprite.RemoveLayer(i);
+            var layer = new PrototypeLayerData();
+            layer.RsiPath = holopadhologram.RsiPath;
+            layer.State = holopadhologram.RsiState;
 
-        if (layerData == null || layerData.Length == 0)
-        {
-            layerData = new PrototypeLayerData[1];
-            layerData[0] = new PrototypeLayerData()
-            {
-                RsiPath = holopadhologram.RsiPath,
-                State = holopadhologram.RsiState
-            };
+            hologramSprite.AddLayer(layer);
         }
 
-        for (int i = 0; i < layerData.Length; i++)
-        {
-            var layer = layerData[i];
-            layer.Shader = "unshaded";
+        // Override specific values
+        hologramSprite.Color = Color.White;
+        hologramSprite.Offset = holopadhologram.Offset;
+        hologramSprite.DrawDepth = (int)DrawDepth.Mobs;
+        hologramSprite.NoRotation = true;
+        hologramSprite.DirectionOverride = Direction.South;
+        hologramSprite.EnableDirectionOverride = true;
 
-            hologramSprite.AddLayer(layerData[i], i);
+        // Remove shading from all layers (except displacement maps)
+        for (int i = 0; i < hologramSprite.AllLayers.Count(); i++)
+        {
+            if (hologramSprite.TryGetLayer(i, out var layer) && layer.ShaderPrototype != "DisplacedStencilDraw")
+                hologramSprite.LayerSetShader(i, "unshaded");
         }
 
-        UpdateHologramShader(uid, hologramSprite, holopadhologram);
+        UpdateHologramShader(hologram, hologramSprite, holopadhologram);
     }
 
     private void UpdateHologramShader(EntityUid uid, SpriteComponent sprite, HolopadHologramComponent holopadHologram)
index c5e6c15b5163073c389a6d6b5a3f586a4362779c..3e99137a2dee9ca2a418ede0b74edfb2ac89b3ae 100644 (file)
@@ -15,6 +15,7 @@ using Content.Shared.Telephone;
 using Content.Shared.UserInterface;
 using Content.Shared.Verbs;
 using Robust.Server.GameObjects;
+using Robust.Server.GameStates;
 using Robust.Shared.Containers;
 using Robust.Shared.Timing;
 using Robust.Shared.Utility;
@@ -35,22 +36,15 @@ public sealed class HolopadSystem : SharedHolopadSystem
     [Dependency] private readonly ChatSystem _chatSystem = default!;
     [Dependency] private readonly PopupSystem _popupSystem = default!;
     [Dependency] private readonly IGameTiming _timing = default!;
+    [Dependency] private readonly PvsOverrideSystem _pvs = default!;
 
     private float _updateTimer = 1.0f;
-
     private const float UpdateTime = 1.0f;
-    private const float MinTimeBetweenSyncRequests = 0.5f;
-    private TimeSpan _minTimeSpanBetweenSyncRequests;
-
-    private HashSet<EntityUid> _pendingRequestsForSpriteState = new();
-    private HashSet<EntityUid> _recentlyUpdatedHolograms = new();
 
     public override void Initialize()
     {
         base.Initialize();
 
-        _minTimeSpanBetweenSyncRequests = TimeSpan.FromSeconds(MinTimeBetweenSyncRequests);
-
         // Holopad UI and bound user interface messages
         SubscribeLocalEvent<HolopadComponent, BeforeActivatableUIOpenEvent>(OnUIOpen);
         SubscribeLocalEvent<HolopadComponent, HolopadStartNewCallMessage>(OnHolopadStartNewCall);
@@ -68,7 +62,6 @@ public sealed class HolopadSystem : SharedHolopadSystem
 
         // Networked events
         SubscribeNetworkEvent<HolopadUserTypingChangedEvent>(OnTypingChanged);
-        SubscribeNetworkEvent<PlayerSpriteStateMessage>(OnPlayerSpriteStateMessage);
 
         // Component start/shutdown events
         SubscribeLocalEvent<HolopadComponent, ComponentInit>(OnHolopadInit);
@@ -268,16 +261,11 @@ public sealed class HolopadSystem : SharedHolopadSystem
         if (source.Comp.Hologram == null)
             GenerateHologram(source);
 
-        // Receiver holopad holograms have to be generated now instead of waiting for their own event
-        // to fire because holographic avatars get synced immediately
         if (TryComp<HolopadComponent>(args.Receiver, out var receivingHolopad) && receivingHolopad.Hologram == null)
             GenerateHologram((args.Receiver, receivingHolopad));
 
-        if (source.Comp.User != null)
-        {
-            // Re-link the user to refresh the sprite data
-            LinkHolopadToUser(source, source.Comp.User.Value);
-        }
+        // Re-link the user to refresh the sprite data
+        LinkHolopadToUser(source, source.Comp.User);
     }
 
     private void OnHoloCallEnded(Entity<HolopadComponent> entity, ref TelephoneCallEndedEvent args)
@@ -323,22 +311,6 @@ public sealed class HolopadSystem : SharedHolopadSystem
         }
     }
 
-    private void OnPlayerSpriteStateMessage(PlayerSpriteStateMessage ev, EntitySessionEventArgs args)
-    {
-        var uid = args.SenderSession.AttachedEntity;
-
-        if (!Exists(uid))
-            return;
-
-        if (!_pendingRequestsForSpriteState.Remove(uid.Value))
-            return;
-
-        if (!TryComp<HolopadUserComponent>(uid, out var holopadUser))
-            return;
-
-        SyncHolopadUserWithLinkedHolograms((uid.Value, holopadUser), ev.SpriteLayerData);
-    }
-
     #endregion
 
     #region: Component start/shutdown events
@@ -485,8 +457,6 @@ public sealed class HolopadSystem : SharedHolopadSystem
                 }
             }
         }
-
-        _recentlyUpdatedHolograms.Clear();
     }
 
     public void UpdateUIState(Entity<HolopadComponent> entity, TelephoneComponent? telephone = null)
@@ -555,10 +525,16 @@ public sealed class HolopadSystem : SharedHolopadSystem
         QueueDel(hologram);
     }
 
-    private void LinkHolopadToUser(Entity<HolopadComponent> entity, EntityUid user)
+    private void LinkHolopadToUser(Entity<HolopadComponent> entity, EntityUid? user)
     {
+        if (user == null)
+        {
+            UnlinkHolopadFromUser(entity, null);
+            return;
+        }
+
         if (!TryComp<HolopadUserComponent>(user, out var holopadUser))
-            holopadUser = AddComp<HolopadUserComponent>(user);
+            holopadUser = AddComp<HolopadUserComponent>(user.Value);
 
         if (user != entity.Comp.User?.Owner)
         {
@@ -567,51 +543,44 @@ public sealed class HolopadSystem : SharedHolopadSystem
 
             // Assigns the new user in their place
             holopadUser.LinkedHolopads.Add(entity);
-            entity.Comp.User = (user, holopadUser);
+            entity.Comp.User = (user.Value, holopadUser);
         }
 
-        if (TryComp<HolographicAvatarComponent>(user, out var avatar))
-        {
-            SyncHolopadUserWithLinkedHolograms((user, holopadUser), avatar.LayerData);
-            return;
-        }
-
-        // We have no apriori sprite data for the hologram, request
-        // the current appearance of the user from the client
-        RequestHolopadUserSpriteUpdate((user, holopadUser));
+        // Add the new user to PVS and sync their appearance with any 
+        // holopads connected to the one they are using
+        _pvs.AddGlobalOverride(user.Value);
+        SyncHolopadHologramAppearanceWithTarget(entity, entity.Comp.User);
     }
 
     private void UnlinkHolopadFromUser(Entity<HolopadComponent> entity, Entity<HolopadUserComponent>? user)
     {
-        if (user == null)
-            return;
-
         entity.Comp.User = null;
+        SyncHolopadHologramAppearanceWithTarget(entity, null);
 
-        foreach (var linkedHolopad in GetLinkedHolopads(entity))
-        {
-            if (linkedHolopad.Comp.Hologram != null)
-            {
-                _appearanceSystem.SetData(linkedHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.IsTyping, false);
-
-                // Send message with no sprite data to the client
-                // This will set the holgram sprite to a generic icon
-                var ev = new PlayerSpriteStateMessage(GetNetEntity(linkedHolopad.Comp.Hologram.Value));
-                RaiseNetworkEvent(ev);
-            }
-        }
-
-        if (!HasComp<HolopadUserComponent>(user))
+        if (user == null)
             return;
 
         user.Value.Comp.LinkedHolopads.Remove(entity);
 
-        if (!user.Value.Comp.LinkedHolopads.Any())
+        if (!user.Value.Comp.LinkedHolopads.Any() &&
+            user.Value.Comp.LifeStage < ComponentLifeStage.Stopping)
         {
-            _pendingRequestsForSpriteState.Remove(user.Value);
+            _pvs.RemoveGlobalOverride(user.Value);
+            RemComp<HolopadUserComponent>(user.Value);
+        }
+    }
+    private void SyncHolopadHologramAppearanceWithTarget(Entity<HolopadComponent> entity, Entity<HolopadUserComponent>? user)
+    {
+        foreach (var linkedHolopad in GetLinkedHolopads(entity))
+        {
+            if (linkedHolopad.Comp.Hologram == null)
+                continue;
+
+            if (user == null)
+                _appearanceSystem.SetData(linkedHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.IsTyping, false);
 
-            if (user.Value.Comp.LifeStage < ComponentLifeStage.Stopping)
-                RemComp<HolopadUserComponent>(user.Value);
+            linkedHolopad.Comp.Hologram.Value.Comp.LinkedEntity = user;
+            Dirty(linkedHolopad.Comp.Hologram.Value);
         }
     }
 
@@ -646,31 +615,6 @@ public sealed class HolopadSystem : SharedHolopadSystem
         Dirty(entity);
     }
 
-    private void RequestHolopadUserSpriteUpdate(Entity<HolopadUserComponent> user)
-    {
-        if (!_pendingRequestsForSpriteState.Add(user))
-            return;
-
-        var ev = new PlayerSpriteStateRequest(GetNetEntity(user));
-        RaiseNetworkEvent(ev);
-    }
-
-    private void SyncHolopadUserWithLinkedHolograms(Entity<HolopadUserComponent> entity, PrototypeLayerData[]? spriteLayerData)
-    {
-        foreach (var linkedHolopad in entity.Comp.LinkedHolopads)
-        {
-            foreach (var receivingHolopad in GetLinkedHolopads(linkedHolopad))
-            {
-                if (receivingHolopad.Comp.Hologram == null || !_recentlyUpdatedHolograms.Add(receivingHolopad.Comp.Hologram.Value))
-                    continue;
-
-                var netHologram = GetNetEntity(receivingHolopad.Comp.Hologram.Value);
-                var ev = new PlayerSpriteStateMessage(netHologram, spriteLayerData);
-                RaiseNetworkEvent(ev);
-            }
-        }
-    }
-
     private void ActivateProjector(Entity<HolopadComponent> entity, EntityUid user)
     {
         if (!TryComp<TelephoneComponent>(entity, out var receiverTelephone))
index be7f5bcb91fcec2f1d0c28a499fdae67ec93e7db..cff71f4fb2828efa969e0b3573438acd29b4523a 100644 (file)
@@ -2,12 +2,12 @@ using Robust.Shared.GameStates;
 
 namespace Content.Shared.Holopad;
 
-[RegisterComponent, NetworkedComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
 public sealed partial class HolographicAvatarComponent : Component
 {
     /// <summary>
     /// The prototype sprite layer data for the hologram
     /// </summary>
-    [DataField]
+    [DataField, AutoNetworkedField]
     public PrototypeLayerData[] LayerData;
 }
index a75ae2762343f2b84cda69bdc33f50b93ed02393..0b650d9b3dcf451fc35c6005a9d91ddeb7b60e2f 100644 (file)
@@ -1,4 +1,5 @@
 using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
 using System.Numerics;
 
 namespace Content.Shared.Holopad;
@@ -6,7 +7,7 @@ namespace Content.Shared.Holopad;
 /// <summary>
 /// Holds data pertaining to holopad holograms
 /// </summary>
-[RegisterComponent, NetworkedComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
 public sealed partial class HolopadHologramComponent : Component
 {
     /// <summary>
@@ -64,8 +65,8 @@ public sealed partial class HolopadHologramComponent : Component
     public Vector2 Offset = new Vector2();
 
     /// <summary>
-    /// A user that are linked to this hologram
+    /// An entity that is linked to this hologram
     /// </summary>
-    [ViewVariables]
-    public Entity<HolopadComponent>? LinkedHolopad;
+    [ViewVariables, AutoNetworkedField]
+    public EntityUid? LinkedEntity = null;
 }
index 9ff20c2e7b35837168d9112aaf17008ae8bfbf6e..c9c2a8828b2022e2c8911c18ce17a66deb8eb1a9 100644 (file)
@@ -20,29 +20,6 @@ public sealed partial class HolopadUserComponent : Component
     public HashSet<Entity<HolopadComponent>> LinkedHolopads = new();
 }
 
-/// <summary>
-/// A networked event raised when the visual state of a hologram is being updated
-/// </summary>
-[Serializable, NetSerializable]
-public sealed class HolopadHologramVisualsUpdateEvent : EntityEventArgs
-{
-    /// <summary>
-    /// The hologram being updated
-    /// </summary>
-    public readonly NetEntity Hologram;
-
-    /// <summary>
-    /// The target the hologram is copying
-    /// </summary>
-    public readonly NetEntity? Target;
-
-    public HolopadHologramVisualsUpdateEvent(NetEntity hologram, NetEntity? target = null)
-    {
-        Hologram = hologram;
-        Target = target;
-    }
-}
-
 /// <summary>
 /// A networked event raised when the visual state of a hologram is being updated
 /// </summary>
@@ -65,40 +42,3 @@ public sealed class HolopadUserTypingChangedEvent : EntityEventArgs
         IsTyping = isTyping;
     }
 }
-
-/// <summary>
-/// A networked event raised by the server to request the current visual state of a target player entity
-/// </summary>
-[Serializable, NetSerializable]
-public sealed class PlayerSpriteStateRequest : EntityEventArgs
-{
-    /// <summary>
-    /// The player entity in question
-    /// </summary>
-    public readonly NetEntity TargetPlayer;
-
-    public PlayerSpriteStateRequest(NetEntity targetPlayer)
-    {
-        TargetPlayer = targetPlayer;
-    }
-}
-
-/// <summary>
-/// The client's response to a <see cref="PlayerSpriteStateRequest"/>
-/// </summary>
-[Serializable, NetSerializable]
-public sealed class PlayerSpriteStateMessage : EntityEventArgs
-{
-    public readonly NetEntity SpriteEntity;
-
-    /// <summary>
-    /// Data needed to reconstruct the player's sprite component layers
-    /// </summary>
-    public readonly PrototypeLayerData[]? SpriteLayerData;
-
-    public PlayerSpriteStateMessage(NetEntity spriteEntity, PrototypeLayerData[]? spriteLayerData = null)
-    {
-        SpriteEntity = spriteEntity;
-        SpriteLayerData = spriteLayerData;
-    }
-}
index 0dfe45c5ae40ca543e278f3f4379b17292b5067e..421d2809d4a01ef34ec9d55a0afe84e81adbbe60 100644 (file)
   components:
   - type: Transform
     anchored: true
-  - type: Sprite
-    noRot: true
-    drawdepth: Mobs
-    offset: -0.02, 0.45
-    overrideDir: South
-    enableOverrideDir: true
+  - type: Sprite # Sprite data is dynamically set in Client.HolopadSystem
   - type: Appearance
   - type: TypingIndicator
     proto: robot
     alpha: 0.9
     intensity: 2
     scrollRate: 0.125
+    offset: -0.02, 0.45
   - type: Tag
     tags:
     - HideContextMenu