From fbf1d476f224ea58a694ed503a7888a0f269f082 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 13 Jul 2023 20:20:46 +1000 Subject: [PATCH] Component ECS (#17991) --- Content.Client/Alerts/ClientAlertsSystem.cs | 8 ++- .../EntitySystems/GasTileOverlaySystem.cs | 3 +- .../Lobby/UI/LobbyCharacterPreviewPanel.cs | 1 + .../Networking/SimplePredictReconcileTest.cs | 39 +++++------ .../Components/HyposprayComponent.cs | 15 +---- .../EntitySystems/ChemistrySystemHypospray.cs | 11 +++- .../Components/SharedHyposprayComponent.cs | 33 +++++----- .../Components/HandVirtualItemComponent.cs | 64 ++++--------------- .../SharedHandsSystem.Virtual.cs | 18 ++++++ .../Hands/EntitySystems/SharedHandsSystem.cs | 5 +- .../Hands/SharedHandVirtualItemSystem.cs | 1 + .../Alert/ServerAlertsComponentTests.cs | 19 ++++-- .../Gamestates/ComponentStateNullTest.cs | 5 +- 13 files changed, 108 insertions(+), 114 deletions(-) create mode 100644 Content.Shared/Hands/EntitySystems/SharedHandsSystem.Virtual.cs diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs index 0008353172..17f6edb95f 100644 --- a/Content.Client/Alerts/ClientAlertsSystem.cs +++ b/Content.Client/Alerts/ClientAlertsSystem.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Shared.Alert; +using Content.Shared.Mobs.Systems; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.Player; @@ -34,7 +35,7 @@ public sealed class ClientAlertsSystem : AlertsSystem AlertOrder = _prototypeManager.EnumeratePrototypes().FirstOrDefault(); if (AlertOrder == null) - Logger.ErrorS("alert", "no alertOrder prototype found, alerts will be in random order"); + Log.Error("alert", "no alertOrder prototype found, alerts will be in random order"); } public IReadOnlyDictionary? ActiveAlerts @@ -67,9 +68,10 @@ public sealed class ClientAlertsSystem : AlertsSystem private void ClientAlertsHandleState(EntityUid uid, AlertsComponent component, ref ComponentHandleState args) { var componentAlerts = (args.Current as AlertsComponentState)?.Alerts; - if (componentAlerts == null) return; + if (componentAlerts == null) + return; - component.Alerts = new(componentAlerts); + component.Alerts = new Dictionary(componentAlerts); if (_playerManager.LocalPlayer?.ControlledEntity == uid) SyncAlerts?.Invoke(this, componentAlerts); diff --git a/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs index a7a2f8d904..ddb0a34085 100644 --- a/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -31,10 +31,9 @@ namespace Content.Client.Atmos.EntitySystems public override void Shutdown() { base.Shutdown(); - _overlayMan.RemoveOverlay(_overlay); + _overlayMan.RemoveOverlay(); } - private void OnHandleState(EntityUid gridUid, GasTileOverlayComponent comp, ref ComponentHandleState args) { if (args.Current is not GasTileOverlayState state) diff --git a/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs b/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs index bd51c6010f..ff33bbadba 100644 --- a/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs +++ b/Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Numerics; +using Content.Client.Alerts; using Content.Client.Humanoid; using Content.Client.Inventory; using Content.Client.Preferences; diff --git a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs index e0c4c607fa..fd9ea03ac3 100644 --- a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs +++ b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs @@ -396,21 +396,11 @@ namespace Content.IntegrationTests.Tests.Networking public sealed class PredictionTestComponent : Component { public bool Foo; + } - public override ComponentState GetComponentState() - { - return new PredictionComponentState(Foo); - } - - public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) - { - if (curState is not PredictionComponentState state) - return; - - Foo = state.Foo; - Dirty(); - } - + [Reflect(false)] + public sealed class PredictionTestEntitySystem : EntitySystem + { [Serializable, NetSerializable] private sealed class PredictionComponentState : ComponentState { @@ -421,11 +411,7 @@ namespace Content.IntegrationTests.Tests.Networking Foo = foo; } } - } - [Reflect(false)] - public sealed class PredictionTestEntitySystem : EntitySystem - { public bool Allow { get; set; } = true; // Queue of all the events that come in so we can test that they come in perfectly as expected. @@ -440,6 +426,23 @@ namespace Content.IntegrationTests.Tests.Networking SubscribeNetworkEvent(HandleMessage); SubscribeLocalEvent(HandleMessage); + + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + } + + private void OnHandleState(EntityUid uid, PredictionTestComponent component, ref ComponentHandleState args) + { + if (args.Current is not PredictionComponentState state) + return; + + component.Foo = state.Foo; + Dirty(component); + } + + private void OnGetState(EntityUid uid, PredictionTestComponent component, ref ComponentGetState args) + { + args.State = new PredictionComponentState(component.Foo); } private void HandleMessage(SetFooMessage message, EntitySessionEventArgs args) diff --git a/Content.Server/Chemistry/Components/HyposprayComponent.cs b/Content.Server/Chemistry/Components/HyposprayComponent.cs index 6b9ff6cb1c..78adadefed 100644 --- a/Content.Server/Chemistry/Components/HyposprayComponent.cs +++ b/Content.Server/Chemistry/Components/HyposprayComponent.cs @@ -8,25 +8,16 @@ namespace Content.Server.Chemistry.Components [RegisterComponent] public sealed class HyposprayComponent : SharedHyposprayComponent { - [Dependency] private readonly IEntityManager _entMan = default!; - + // TODO: This should be on clumsycomponent. [DataField("clumsyFailChance")] [ViewVariables(VVAccess.ReadWrite)] - public float ClumsyFailChance { get; set; } = 0.5f; + public float ClumsyFailChance = 0.5f; [DataField("transferAmount")] [ViewVariables(VVAccess.ReadWrite)] - public FixedPoint2 TransferAmount { get; set; } = FixedPoint2.New(5); + public FixedPoint2 TransferAmount = FixedPoint2.New(5); [DataField("injectSound")] public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg"); - - public override ComponentState GetComponentState() - { - var solutionSys = _entMan.EntitySysManager.GetEntitySystem(); - return solutionSys.TryGetSolution(Owner, SolutionName, out var solution) - ? new HyposprayComponentState(solution.Volume, solution.MaxVolume) - : new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero); - } } } diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs index 2f579e0594..d357e46f66 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemistrySystemHypospray.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Diagnostics.CodeAnalysis; using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components.SolutionManager; -// using Content.Server.Weapons.Melee; +using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Database; using Content.Shared.FixedPoint; @@ -12,6 +12,7 @@ using Content.Shared.Interaction.Events; using Content.Shared.Mobs.Components; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Timing; +using Robust.Shared.GameStates; namespace Content.Server.Chemistry.EntitySystems { @@ -25,6 +26,14 @@ namespace Content.Server.Chemistry.EntitySystems SubscribeLocalEvent(OnAttack); SubscribeLocalEvent(OnSolutionChange); SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnHypoGetState); + } + + private void OnHypoGetState(EntityUid uid, HyposprayComponent component, ref ComponentGetState args) + { + args.State = _solutions.TryGetSolution(uid, component.SolutionName, out var solution) + ? new HyposprayComponentState(solution.Volume, solution.MaxVolume) + : new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero); } private void OnUseInHand(EntityUid uid, HyposprayComponent component, UseInHandEvent args) diff --git a/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs b/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs index 59d3192cfb..f93dc0748b 100644 --- a/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs +++ b/Content.Shared/Chemistry/Components/SharedHyposprayComponent.cs @@ -2,25 +2,24 @@ using Content.Shared.FixedPoint; using Robust.Shared.GameStates; using Robust.Shared.Serialization; -namespace Content.Shared.Chemistry.Components +namespace Content.Shared.Chemistry.Components; + +[NetworkedComponent()] +public abstract class SharedHyposprayComponent : Component { - [NetworkedComponent()] - public abstract class SharedHyposprayComponent : Component - { - [DataField("solutionName")] - public string SolutionName = "hypospray"; - } + [DataField("solutionName")] + public string SolutionName = "hypospray"; +} - [Serializable, NetSerializable] - public sealed class HyposprayComponentState : ComponentState - { - public FixedPoint2 CurVolume { get; } - public FixedPoint2 MaxVolume { get; } +[Serializable, NetSerializable] +public sealed class HyposprayComponentState : ComponentState +{ + public FixedPoint2 CurVolume { get; } + public FixedPoint2 MaxVolume { get; } - public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume) - { - CurVolume = curVolume; - MaxVolume = maxVolume; - } + public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume) + { + CurVolume = curVolume; + MaxVolume = maxVolume; } } diff --git a/Content.Shared/Hands/Components/HandVirtualItemComponent.cs b/Content.Shared/Hands/Components/HandVirtualItemComponent.cs index e9e6bfe03a..e456013188 100644 --- a/Content.Shared/Hands/Components/HandVirtualItemComponent.cs +++ b/Content.Shared/Hands/Components/HandVirtualItemComponent.cs @@ -1,55 +1,15 @@ -using Content.Shared.Item; -using Robust.Shared.Containers; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; +using Robust.Shared.GameStates; -namespace Content.Shared.Hands.Components +namespace Content.Shared.Hands.Components; + +[RegisterComponent] +[NetworkedComponent] +[AutoGenerateComponentState(true)] +public sealed partial class HandVirtualItemComponent : Component { - [RegisterComponent] - [NetworkedComponent] - public sealed class HandVirtualItemComponent : Component - { - private EntityUid _blockingEntity; - - /// - /// The entity blocking this hand. - /// - public EntityUid BlockingEntity - { - get => _blockingEntity; - set - { - _blockingEntity = value; - Dirty(); - } - } - - public override ComponentState GetComponentState() - { - return new VirtualItemComponentState(BlockingEntity); - } - - public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) - { - if (curState is not VirtualItemComponentState pullState) - return; - - _blockingEntity = pullState.BlockingEntity; - - // update hands GUI with new entity. - if (Owner.TryGetContainer(out _)) - EntitySystem.Get().VisualsChanged(Owner); - } - - [Serializable, NetSerializable] - public sealed class VirtualItemComponentState : ComponentState - { - public readonly EntityUid BlockingEntity; - - public VirtualItemComponentState(EntityUid blockingEntity) - { - BlockingEntity = blockingEntity; - } - } - } + /// + /// The entity blocking this hand. + /// + [DataField("blockingEntity"), AutoNetworkedField] + public EntityUid BlockingEntity; } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Virtual.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Virtual.cs new file mode 100644 index 0000000000..36498d929b --- /dev/null +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Virtual.cs @@ -0,0 +1,18 @@ +using Content.Shared.Hands.Components; + +namespace Content.Shared.Hands.EntitySystems; + +public abstract partial class SharedHandsSystem +{ + private void InitializeVirtual() + { + SubscribeLocalEvent(OnVirtualAfter); + } + + private void OnVirtualAfter(EntityUid uid, HandVirtualItemComponent component, ref AfterAutoHandleStateEvent args) + { + // update hands GUI with new entity. + if (_containerSystem.IsEntityInContainer(uid)) + _items.VisualsChanged(uid); + } +} diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs index 1cb7617a49..b0a3f116cc 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; using Content.Shared.Hands.Components; using Content.Shared.Interaction; +using Content.Shared.Item; using Robust.Shared.Containers; using Robust.Shared.Input.Binding; @@ -12,9 +13,10 @@ namespace Content.Shared.Hands.EntitySystems; public abstract partial class SharedHandsSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly SharedItemSystem _items = default!; protected event Action? OnHandSetActive; @@ -25,6 +27,7 @@ public abstract partial class SharedHandsSystem : EntitySystem InitializeInteractions(); InitializeDrop(); InitializePickup(); + InitializeVirtual(); } public override void Shutdown() diff --git a/Content.Shared/Hands/SharedHandVirtualItemSystem.cs b/Content.Shared/Hands/SharedHandVirtualItemSystem.cs index f93d729298..7fb4a4c127 100644 --- a/Content.Shared/Hands/SharedHandVirtualItemSystem.cs +++ b/Content.Shared/Hands/SharedHandVirtualItemSystem.cs @@ -37,6 +37,7 @@ public abstract class SharedHandVirtualItemSystem : EntitySystem virtualItem = Spawn("HandVirtualItem", pos); var virtualItemComp = EntityManager.GetComponent(virtualItem.Value); virtualItemComp.BlockingEntity = blockingEnt; + Dirty(virtualItemComp); _hands.DoPickup(user, hand, virtualItem.Value); return true; } diff --git a/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs index c47292a82d..83fbb009ea 100644 --- a/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs +++ b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs @@ -3,6 +3,7 @@ using Content.Server.Alert; using Content.Shared.Alert; using NUnit.Framework; using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager; @@ -33,6 +34,7 @@ namespace Content.Tests.Shared.Alert // but wanted to keep it anyway to see what's possible w.r.t. testing components // in a unit test + var entManager = IoCManager.Resolve(); IoCManager.Resolve().Initialize(); var prototypeManager = IoCManager.Resolve(); prototypeManager.Initialize(); @@ -41,7 +43,7 @@ namespace Content.Tests.Shared.Alert prototypeManager.LoadFromStream(new StringReader(PROTOTYPES)); prototypeManager.ResolveResults(); - var entSys = IoCManager.Resolve(); + var entSys = entManager.EntitySysManager; entSys.LoadExtraSystemType(); var alertsComponent = new AlertsComponent(); @@ -51,18 +53,27 @@ namespace Content.Tests.Shared.Alert Assert.That(EntitySystem.Get().TryGet(AlertType.HighPressure, out var highpressure)); EntitySystem.Get().ShowAlert(alertsComponent.Owner, AlertType.LowPressure, null, null); - var alertState = alertsComponent.GetComponentState() as AlertsComponentState; + + var getty = new ComponentGetState(); + entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); + + var alertState = (AlertsComponentState) getty.State!; Assert.NotNull(alertState); Assert.That(alertState.Alerts.Count, Is.EqualTo(1)); Assert.That(alertState.Alerts.ContainsKey(lowpressure.AlertKey)); EntitySystem.Get().ShowAlert(alertsComponent.Owner, AlertType.HighPressure, null, null); - alertState = alertsComponent.GetComponentState() as AlertsComponentState; + + // Lazy + entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); + alertState = (AlertsComponentState) getty.State!; Assert.That(alertState.Alerts.Count, Is.EqualTo(1)); Assert.That(alertState.Alerts.ContainsKey(highpressure.AlertKey)); EntitySystem.Get().ClearAlertCategory(alertsComponent.Owner, AlertCategory.Pressure); - alertState = alertsComponent.GetComponentState() as AlertsComponentState; + + entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); + alertState = (AlertsComponentState) getty.State!; Assert.That(alertState.Alerts.Count, Is.EqualTo(0)); } } diff --git a/Content.Tests/Shared/Gamestates/ComponentStateNullTest.cs b/Content.Tests/Shared/Gamestates/ComponentStateNullTest.cs index 651c9a2585..9306bd54cb 100644 --- a/Content.Tests/Shared/Gamestates/ComponentStateNullTest.cs +++ b/Content.Tests/Shared/Gamestates/ComponentStateNullTest.cs @@ -20,10 +20,7 @@ namespace Content.Tests.Shared.Gamestates foreach (var compType in comps) { // Any component should be able to be instantiated without DI injection. - var compInstance = (IComponent) Activator.CreateInstance(compType); - - // Any component should treat this as a null function. - compInstance.HandleComponentState(null, null); + _ = (IComponent) Activator.CreateInstance(compType); } }