From: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Date: Fri, 17 Jan 2025 20:09:59 +0000 (-0600) Subject: Holopad networking rework (#34112) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=efd5d644e8e5f38811a3778d0c0c73575943a236;p=space-station-14.git Holopad networking rework (#34112) * Initial commit * Finalizing main changes * Addressed reviews * Fixed a few issues * Switched to using global overrides * Removed unnecessary references --- diff --git a/Content.Client/Holopad/HolopadSystem.cs b/Content.Client/Holopad/HolopadSystem.cs index 3bd556f1fc..6aad39fe24 100644 --- a/Content.Client/Holopad/HolopadSystem.cs +++ b/Content.Client/Holopad/HolopadSystem.cs @@ -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(OnComponentInit); + SubscribeLocalEvent(OnComponentStartup); SubscribeLocalEvent(OnShaderRender); SubscribeAllEvent(OnTypingChanged); - - SubscribeNetworkEvent(OnPlayerSpriteStateRequest); - SubscribeNetworkEvent(OnPlayerSpriteStateMessage); } - private void OnComponentInit(EntityUid uid, HolopadHologramComponent component, ComponentInit ev) + private void OnComponentStartup(Entity entity, ref ComponentStartup ev) { - if (!TryComp(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 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(hologram, out var hologramSprite) || + !TryComp(hologram, out var holopadhologram)) return; - if (!TryComp(player, out var playerSprite)) - return; - - var spriteLayerData = new List(); + // Remove all sprite layers + for (int i = hologramSprite.AllLayers.Count() - 1; i >= 0; i--) + hologramSprite.RemoveLayer(i); - if (playerSprite.Visible) + if (TryComp(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(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(uid, out var hologramSprite)) - return; - - if (!TryComp(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) diff --git a/Content.Server/Holopad/HolopadSystem.cs b/Content.Server/Holopad/HolopadSystem.cs index c5e6c15b51..3e99137a2d 100644 --- a/Content.Server/Holopad/HolopadSystem.cs +++ b/Content.Server/Holopad/HolopadSystem.cs @@ -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 _pendingRequestsForSpriteState = new(); - private HashSet _recentlyUpdatedHolograms = new(); public override void Initialize() { base.Initialize(); - _minTimeSpanBetweenSyncRequests = TimeSpan.FromSeconds(MinTimeBetweenSyncRequests); - // Holopad UI and bound user interface messages SubscribeLocalEvent(OnUIOpen); SubscribeLocalEvent(OnHolopadStartNewCall); @@ -68,7 +62,6 @@ public sealed class HolopadSystem : SharedHolopadSystem // Networked events SubscribeNetworkEvent(OnTypingChanged); - SubscribeNetworkEvent(OnPlayerSpriteStateMessage); // Component start/shutdown events SubscribeLocalEvent(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(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 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(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 entity, TelephoneComponent? telephone = null) @@ -555,10 +525,16 @@ public sealed class HolopadSystem : SharedHolopadSystem QueueDel(hologram); } - private void LinkHolopadToUser(Entity entity, EntityUid user) + private void LinkHolopadToUser(Entity entity, EntityUid? user) { + if (user == null) + { + UnlinkHolopadFromUser(entity, null); + return; + } + if (!TryComp(user, out var holopadUser)) - holopadUser = AddComp(user); + holopadUser = AddComp(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(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 entity, Entity? 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(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(user.Value); + } + } + private void SyncHolopadHologramAppearanceWithTarget(Entity entity, Entity? 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(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 user) - { - if (!_pendingRequestsForSpriteState.Add(user)) - return; - - var ev = new PlayerSpriteStateRequest(GetNetEntity(user)); - RaiseNetworkEvent(ev); - } - - private void SyncHolopadUserWithLinkedHolograms(Entity 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 entity, EntityUid user) { if (!TryComp(entity, out var receiverTelephone)) diff --git a/Content.Shared/Holopad/HolographicAvatarComponent.cs b/Content.Shared/Holopad/HolographicAvatarComponent.cs index be7f5bcb91..cff71f4fb2 100644 --- a/Content.Shared/Holopad/HolographicAvatarComponent.cs +++ b/Content.Shared/Holopad/HolographicAvatarComponent.cs @@ -2,12 +2,12 @@ using Robust.Shared.GameStates; namespace Content.Shared.Holopad; -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class HolographicAvatarComponent : Component { /// /// The prototype sprite layer data for the hologram /// - [DataField] + [DataField, AutoNetworkedField] public PrototypeLayerData[] LayerData; } diff --git a/Content.Shared/Holopad/HolopadHologramComponent.cs b/Content.Shared/Holopad/HolopadHologramComponent.cs index a75ae27623..0b650d9b3d 100644 --- a/Content.Shared/Holopad/HolopadHologramComponent.cs +++ b/Content.Shared/Holopad/HolopadHologramComponent.cs @@ -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; /// /// Holds data pertaining to holopad holograms /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class HolopadHologramComponent : Component { /// @@ -64,8 +65,8 @@ public sealed partial class HolopadHologramComponent : Component public Vector2 Offset = new Vector2(); /// - /// A user that are linked to this hologram + /// An entity that is linked to this hologram /// - [ViewVariables] - public Entity? LinkedHolopad; + [ViewVariables, AutoNetworkedField] + public EntityUid? LinkedEntity = null; } diff --git a/Content.Shared/Holopad/HolopadUserComponent.cs b/Content.Shared/Holopad/HolopadUserComponent.cs index 9ff20c2e7b..c9c2a8828b 100644 --- a/Content.Shared/Holopad/HolopadUserComponent.cs +++ b/Content.Shared/Holopad/HolopadUserComponent.cs @@ -20,29 +20,6 @@ public sealed partial class HolopadUserComponent : Component public HashSet> LinkedHolopads = new(); } -/// -/// A networked event raised when the visual state of a hologram is being updated -/// -[Serializable, NetSerializable] -public sealed class HolopadHologramVisualsUpdateEvent : EntityEventArgs -{ - /// - /// The hologram being updated - /// - public readonly NetEntity Hologram; - - /// - /// The target the hologram is copying - /// - public readonly NetEntity? Target; - - public HolopadHologramVisualsUpdateEvent(NetEntity hologram, NetEntity? target = null) - { - Hologram = hologram; - Target = target; - } -} - /// /// A networked event raised when the visual state of a hologram is being updated /// @@ -65,40 +42,3 @@ public sealed class HolopadUserTypingChangedEvent : EntityEventArgs IsTyping = isTyping; } } - -/// -/// A networked event raised by the server to request the current visual state of a target player entity -/// -[Serializable, NetSerializable] -public sealed class PlayerSpriteStateRequest : EntityEventArgs -{ - /// - /// The player entity in question - /// - public readonly NetEntity TargetPlayer; - - public PlayerSpriteStateRequest(NetEntity targetPlayer) - { - TargetPlayer = targetPlayer; - } -} - -/// -/// The client's response to a -/// -[Serializable, NetSerializable] -public sealed class PlayerSpriteStateMessage : EntityEventArgs -{ - public readonly NetEntity SpriteEntity; - - /// - /// Data needed to reconstruct the player's sprite component layers - /// - public readonly PrototypeLayerData[]? SpriteLayerData; - - public PlayerSpriteStateMessage(NetEntity spriteEntity, PrototypeLayerData[]? spriteLayerData = null) - { - SpriteEntity = spriteEntity; - SpriteLayerData = spriteLayerData; - } -} diff --git a/Resources/Prototypes/Entities/Structures/Machines/holopad.yml b/Resources/Prototypes/Entities/Structures/Machines/holopad.yml index 0dfe45c5ae..421d2809d4 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/holopad.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/holopad.yml @@ -155,12 +155,7 @@ 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 @@ -177,6 +172,7 @@ alpha: 0.9 intensity: 2 scrollRate: 0.125 + offset: -0.02, 0.45 - type: Tag tags: - HideContextMenu