Component ECS (#17991)

This commit is contained in:
metalgearsloth
2023-07-13 20:20:46 +10:00
committed by GitHub
parent 0c93be1dcd
commit fbf1d476f2
13 changed files with 108 additions and 114 deletions

View File

@@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Mobs.Systems;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Player; using Robust.Client.Player;
@@ -34,7 +35,7 @@ public sealed class ClientAlertsSystem : AlertsSystem
AlertOrder = _prototypeManager.EnumeratePrototypes<AlertOrderPrototype>().FirstOrDefault(); AlertOrder = _prototypeManager.EnumeratePrototypes<AlertOrderPrototype>().FirstOrDefault();
if (AlertOrder == null) 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<AlertKey, AlertState>? ActiveAlerts public IReadOnlyDictionary<AlertKey, AlertState>? ActiveAlerts
@@ -67,9 +68,10 @@ public sealed class ClientAlertsSystem : AlertsSystem
private void ClientAlertsHandleState(EntityUid uid, AlertsComponent component, ref ComponentHandleState args) private void ClientAlertsHandleState(EntityUid uid, AlertsComponent component, ref ComponentHandleState args)
{ {
var componentAlerts = (args.Current as AlertsComponentState)?.Alerts; var componentAlerts = (args.Current as AlertsComponentState)?.Alerts;
if (componentAlerts == null) return; if (componentAlerts == null)
return;
component.Alerts = new(componentAlerts); component.Alerts = new Dictionary<AlertKey, AlertState>(componentAlerts);
if (_playerManager.LocalPlayer?.ControlledEntity == uid) if (_playerManager.LocalPlayer?.ControlledEntity == uid)
SyncAlerts?.Invoke(this, componentAlerts); SyncAlerts?.Invoke(this, componentAlerts);

View File

@@ -31,10 +31,9 @@ namespace Content.Client.Atmos.EntitySystems
public override void Shutdown() public override void Shutdown()
{ {
base.Shutdown(); base.Shutdown();
_overlayMan.RemoveOverlay(_overlay); _overlayMan.RemoveOverlay<GasTileOverlay>();
} }
private void OnHandleState(EntityUid gridUid, GasTileOverlayComponent comp, ref ComponentHandleState args) private void OnHandleState(EntityUid gridUid, GasTileOverlayComponent comp, ref ComponentHandleState args)
{ {
if (args.Current is not GasTileOverlayState state) if (args.Current is not GasTileOverlayState state)

View File

@@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Client.Alerts;
using Content.Client.Humanoid; using Content.Client.Humanoid;
using Content.Client.Inventory; using Content.Client.Inventory;
using Content.Client.Preferences; using Content.Client.Preferences;

View File

@@ -396,21 +396,11 @@ namespace Content.IntegrationTests.Tests.Networking
public sealed class PredictionTestComponent : Component public sealed class PredictionTestComponent : Component
{ {
public bool Foo; public bool Foo;
}
public override ComponentState GetComponentState() [Reflect(false)]
{ public sealed class PredictionTestEntitySystem : EntitySystem
return new PredictionComponentState(Foo); {
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{
if (curState is not PredictionComponentState state)
return;
Foo = state.Foo;
Dirty();
}
[Serializable, NetSerializable] [Serializable, NetSerializable]
private sealed class PredictionComponentState : ComponentState private sealed class PredictionComponentState : ComponentState
{ {
@@ -421,11 +411,7 @@ namespace Content.IntegrationTests.Tests.Networking
Foo = foo; Foo = foo;
} }
} }
}
[Reflect(false)]
public sealed class PredictionTestEntitySystem : EntitySystem
{
public bool Allow { get; set; } = true; 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. // 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<SetFooMessage>(HandleMessage); SubscribeNetworkEvent<SetFooMessage>(HandleMessage);
SubscribeLocalEvent<SetFooMessage>(HandleMessage); SubscribeLocalEvent<SetFooMessage>(HandleMessage);
SubscribeLocalEvent<PredictionTestComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<PredictionTestComponent, ComponentHandleState>(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) private void HandleMessage(SetFooMessage message, EntitySessionEventArgs args)

View File

@@ -8,25 +8,16 @@ namespace Content.Server.Chemistry.Components
[RegisterComponent] [RegisterComponent]
public sealed class HyposprayComponent : SharedHyposprayComponent public sealed class HyposprayComponent : SharedHyposprayComponent
{ {
[Dependency] private readonly IEntityManager _entMan = default!; // TODO: This should be on clumsycomponent.
[DataField("clumsyFailChance")] [DataField("clumsyFailChance")]
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public float ClumsyFailChance { get; set; } = 0.5f; public float ClumsyFailChance = 0.5f;
[DataField("transferAmount")] [DataField("transferAmount")]
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 TransferAmount { get; set; } = FixedPoint2.New(5); public FixedPoint2 TransferAmount = FixedPoint2.New(5);
[DataField("injectSound")] [DataField("injectSound")]
public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg"); public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg");
public override ComponentState GetComponentState()
{
var solutionSys = _entMan.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
return solutionSys.TryGetSolution(Owner, SolutionName, out var solution)
? new HyposprayComponentState(solution.Volume, solution.MaxVolume)
: new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero);
}
} }
} }

View File

@@ -2,7 +2,7 @@ using System.Linq;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.Components.SolutionManager;
// using Content.Server.Weapons.Melee; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
@@ -12,6 +12,7 @@ using Content.Shared.Interaction.Events;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Timing; using Content.Shared.Timing;
using Robust.Shared.GameStates;
namespace Content.Server.Chemistry.EntitySystems namespace Content.Server.Chemistry.EntitySystems
{ {
@@ -25,6 +26,14 @@ namespace Content.Server.Chemistry.EntitySystems
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack); SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
SubscribeLocalEvent<HyposprayComponent, SolutionChangedEvent>(OnSolutionChange); SubscribeLocalEvent<HyposprayComponent, SolutionChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand); SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<HyposprayComponent, ComponentGetState>(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) private void OnUseInHand(EntityUid uid, HyposprayComponent component, UseInHandEvent args)

View File

@@ -2,25 +2,24 @@ using Content.Shared.FixedPoint;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Chemistry.Components namespace Content.Shared.Chemistry.Components;
[NetworkedComponent()]
public abstract class SharedHyposprayComponent : Component
{ {
[NetworkedComponent()] [DataField("solutionName")]
public abstract class SharedHyposprayComponent : Component public string SolutionName = "hypospray";
{ }
[DataField("solutionName")]
public string SolutionName = "hypospray";
}
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class HyposprayComponentState : ComponentState public sealed class HyposprayComponentState : ComponentState
{ {
public FixedPoint2 CurVolume { get; } public FixedPoint2 CurVolume { get; }
public FixedPoint2 MaxVolume { get; } public FixedPoint2 MaxVolume { get; }
public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume) public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume)
{ {
CurVolume = curVolume; CurVolume = curVolume;
MaxVolume = maxVolume; MaxVolume = maxVolume;
}
} }
} }

View File

@@ -1,55 +1,15 @@
using Content.Shared.Item; using Robust.Shared.GameStates;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Hands.Components namespace Content.Shared.Hands.Components;
[RegisterComponent]
[NetworkedComponent]
[AutoGenerateComponentState(true)]
public sealed partial class HandVirtualItemComponent : Component
{ {
[RegisterComponent] /// <summary>
[NetworkedComponent] /// The entity blocking this hand.
public sealed class HandVirtualItemComponent : Component /// </summary>
{ [DataField("blockingEntity"), AutoNetworkedField]
private EntityUid _blockingEntity; public EntityUid BlockingEntity;
/// <summary>
/// The entity blocking this hand.
/// </summary>
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<SharedItemSystem>().VisualsChanged(Owner);
}
[Serializable, NetSerializable]
public sealed class VirtualItemComponentState : ComponentState
{
public readonly EntityUid BlockingEntity;
public VirtualItemComponentState(EntityUid blockingEntity)
{
BlockingEntity = blockingEntity;
}
}
}
} }

View File

@@ -0,0 +1,18 @@
using Content.Shared.Hands.Components;
namespace Content.Shared.Hands.EntitySystems;
public abstract partial class SharedHandsSystem
{
private void InitializeVirtual()
{
SubscribeLocalEvent<HandVirtualItemComponent, AfterAutoHandleStateEvent>(OnVirtualAfter);
}
private void OnVirtualAfter(EntityUid uid, HandVirtualItemComponent component, ref AfterAutoHandleStateEvent args)
{
// update hands GUI with new entity.
if (_containerSystem.IsEntityInContainer(uid))
_items.VisualsChanged(uid);
}
}

View File

@@ -4,6 +4,7 @@ using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Item;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Input.Binding; using Robust.Shared.Input.Binding;
@@ -12,9 +13,10 @@ namespace Content.Shared.Hands.EntitySystems;
public abstract partial class SharedHandsSystem : EntitySystem public abstract partial class SharedHandsSystem : EntitySystem
{ {
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly SharedItemSystem _items = default!;
protected event Action<HandsComponent?>? OnHandSetActive; protected event Action<HandsComponent?>? OnHandSetActive;
@@ -25,6 +27,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
InitializeInteractions(); InitializeInteractions();
InitializeDrop(); InitializeDrop();
InitializePickup(); InitializePickup();
InitializeVirtual();
} }
public override void Shutdown() public override void Shutdown()

View File

@@ -37,6 +37,7 @@ public abstract class SharedHandVirtualItemSystem : EntitySystem
virtualItem = Spawn("HandVirtualItem", pos); virtualItem = Spawn("HandVirtualItem", pos);
var virtualItemComp = EntityManager.GetComponent<HandVirtualItemComponent>(virtualItem.Value); var virtualItemComp = EntityManager.GetComponent<HandVirtualItemComponent>(virtualItem.Value);
virtualItemComp.BlockingEntity = blockingEnt; virtualItemComp.BlockingEntity = blockingEnt;
Dirty(virtualItemComp);
_hands.DoPickup(user, hand, virtualItem.Value); _hands.DoPickup(user, hand, virtualItem.Value);
return true; return true;
} }

View File

@@ -3,6 +3,7 @@ using Content.Server.Alert;
using Content.Shared.Alert; using Content.Shared.Alert;
using NUnit.Framework; using NUnit.Framework;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager; 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 // but wanted to keep it anyway to see what's possible w.r.t. testing components
// in a unit test // in a unit test
var entManager = IoCManager.Resolve<IEntityManager>();
IoCManager.Resolve<ISerializationManager>().Initialize(); IoCManager.Resolve<ISerializationManager>().Initialize();
var prototypeManager = IoCManager.Resolve<IPrototypeManager>(); var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
prototypeManager.Initialize(); prototypeManager.Initialize();
@@ -41,7 +43,7 @@ namespace Content.Tests.Shared.Alert
prototypeManager.LoadFromStream(new StringReader(PROTOTYPES)); prototypeManager.LoadFromStream(new StringReader(PROTOTYPES));
prototypeManager.ResolveResults(); prototypeManager.ResolveResults();
var entSys = IoCManager.Resolve<IEntitySystemManager>(); var entSys = entManager.EntitySysManager;
entSys.LoadExtraSystemType<ServerAlertsSystem>(); entSys.LoadExtraSystemType<ServerAlertsSystem>();
var alertsComponent = new AlertsComponent(); var alertsComponent = new AlertsComponent();
@@ -51,18 +53,27 @@ namespace Content.Tests.Shared.Alert
Assert.That(EntitySystem.Get<AlertsSystem>().TryGet(AlertType.HighPressure, out var highpressure)); Assert.That(EntitySystem.Get<AlertsSystem>().TryGet(AlertType.HighPressure, out var highpressure));
EntitySystem.Get<AlertsSystem>().ShowAlert(alertsComponent.Owner, AlertType.LowPressure, null, null); EntitySystem.Get<AlertsSystem>().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.NotNull(alertState);
Assert.That(alertState.Alerts.Count, Is.EqualTo(1)); Assert.That(alertState.Alerts.Count, Is.EqualTo(1));
Assert.That(alertState.Alerts.ContainsKey(lowpressure.AlertKey)); Assert.That(alertState.Alerts.ContainsKey(lowpressure.AlertKey));
EntitySystem.Get<AlertsSystem>().ShowAlert(alertsComponent.Owner, AlertType.HighPressure, null, null); EntitySystem.Get<AlertsSystem>().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.Count, Is.EqualTo(1));
Assert.That(alertState.Alerts.ContainsKey(highpressure.AlertKey)); Assert.That(alertState.Alerts.ContainsKey(highpressure.AlertKey));
EntitySystem.Get<AlertsSystem>().ClearAlertCategory(alertsComponent.Owner, AlertCategory.Pressure); EntitySystem.Get<AlertsSystem>().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)); Assert.That(alertState.Alerts.Count, Is.EqualTo(0));
} }
} }

View File

@@ -20,10 +20,7 @@ namespace Content.Tests.Shared.Gamestates
foreach (var compType in comps) foreach (var compType in comps)
{ {
// Any component should be able to be instantiated without DI injection. // Any component should be able to be instantiated without DI injection.
var compInstance = (IComponent) Activator.CreateInstance(compType); _ = (IComponent) Activator.CreateInstance(compType);
// Any component should treat this as a null function.
compInstance.HandleComponentState(null, null);
} }
} }