diff --git a/Content.Client/Power/Components/ApcPowerReceiverComponent.cs b/Content.Client/Power/Components/ApcPowerReceiverComponent.cs new file mode 100644 index 0000000000..fbebcb7cf8 --- /dev/null +++ b/Content.Client/Power/Components/ApcPowerReceiverComponent.cs @@ -0,0 +1,8 @@ +using Content.Shared.Power.Components; + +namespace Content.Client.Power.Components; + +[RegisterComponent] +public sealed partial class ApcPowerReceiverComponent : SharedApcPowerReceiverComponent +{ +} diff --git a/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs new file mode 100644 index 0000000000..4d56592c23 --- /dev/null +++ b/Content.Client/Power/EntitySystems/PowerReceiverSystem.cs @@ -0,0 +1,23 @@ +using Content.Client.Power.Components; +using Content.Shared.Power.Components; +using Content.Shared.Power.EntitySystems; +using Robust.Shared.GameStates; + +namespace Content.Client.Power.EntitySystems; + +public sealed class PowerReceiverSystem : SharedPowerReceiverSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnHandleState); + } + + private void OnHandleState(EntityUid uid, ApcPowerReceiverComponent component, ref ComponentHandleState args) + { + if (args.Current is not ApcPowerReceiverComponentState state) + return; + + component.Powered = state.Powered; + } +} diff --git a/Content.Server/Power/Components/ApcPowerReceiverComponent.cs b/Content.Server/Power/Components/ApcPowerReceiverComponent.cs index b8340d0be4..9a68e2aabb 100644 --- a/Content.Server/Power/Components/ApcPowerReceiverComponent.cs +++ b/Content.Server/Power/Components/ApcPowerReceiverComponent.cs @@ -1,5 +1,6 @@ using Content.Server.Power.NodeGroups; using Content.Server.Power.Pow3r; +using Content.Shared.Power.Components; namespace Content.Server.Power.Components { @@ -8,11 +9,8 @@ namespace Content.Server.Power.Components /// so that it can receive power from a . /// [RegisterComponent] - public sealed partial class ApcPowerReceiverComponent : Component + public sealed partial class ApcPowerReceiverComponent : SharedApcPowerReceiverComponent { - [ViewVariables] - public bool Powered => (MathHelper.CloseToPercent(NetworkLoad.ReceivingPower, Load) || !NeedsPower) && !PowerDisabled; - /// /// Amount of charge this needs from an APC per second to function. /// @@ -33,7 +31,7 @@ namespace Content.Server.Power.Components { _needsPower = value; // Reset this so next tick will do a power update. - PoweredLastUpdate = null; + Recalculate = true; } } @@ -50,7 +48,8 @@ namespace Content.Server.Power.Components set => NetworkLoad.Enabled = !value; } - public bool? PoweredLastUpdate; + // TODO Is this needed? It forces a PowerChangedEvent when NeedsPower is toggled even if it changes to the same state. + public bool Recalculate; [ViewVariables] public PowerState.Load NetworkLoad { get; } = new PowerState.Load @@ -66,10 +65,5 @@ namespace Content.Server.Power.Components /// Does nothing on the client. /// [ByRefEvent] - public readonly record struct PowerChangedEvent(bool Powered, float ReceivingPower) - { - public readonly bool Powered = Powered; - public readonly float ReceivingPower = ReceivingPower; - } - + public readonly record struct PowerChangedEvent(bool Powered, float ReceivingPower); } diff --git a/Content.Server/Power/EntitySystems/PowerNetSystem.cs b/Content.Server/Power/EntitySystems/PowerNetSystem.cs index 07ecc2eafb..7bd057951c 100644 --- a/Content.Server/Power/EntitySystems/PowerNetSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerNetSystem.cs @@ -19,6 +19,7 @@ namespace Content.Server.Power.EntitySystems [Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly PowerNetConnectorSystem _powerNetConnector = default!; [Dependency] private readonly IParallelManager _parMan = default!; + [Dependency] private readonly PowerReceiverSystem _powerReceiver = default!; private readonly PowerState _powerState = new(); private readonly HashSet _powerNetReconnectQueue = new(); @@ -302,19 +303,27 @@ namespace Content.Server.Power.EntitySystems var enumerator = AllEntityQuery(); while (enumerator.MoveNext(out var uid, out var apcReceiver)) { - var powered = apcReceiver.Powered; - if (powered == apcReceiver.PoweredLastUpdate) + var powered = !apcReceiver.PowerDisabled + && (!apcReceiver.NeedsPower + || MathHelper.CloseToPercent(apcReceiver.NetworkLoad.ReceivingPower, + apcReceiver.Load)); + + // If new value is the same as the old, then exit + if (!apcReceiver.Recalculate && apcReceiver.Powered == powered) continue; - if (metaQuery.GetComponent(uid).EntityPaused) + var metadata = metaQuery.Comp(uid); + if (metadata.EntityPaused) continue; - apcReceiver.PoweredLastUpdate = powered; - var ev = new PowerChangedEvent(apcReceiver.Powered, apcReceiver.NetworkLoad.ReceivingPower); + apcReceiver.Recalculate = false; + apcReceiver.Powered = powered; + Dirty(uid, apcReceiver, metadata); + var ev = new PowerChangedEvent(powered, apcReceiver.NetworkLoad.ReceivingPower); RaiseLocalEvent(uid, ref ev); - if (appearanceQuery.TryGetComponent(uid, out var appearance)) + if (appearanceQuery.TryComp(uid, out var appearance)) _appearance.SetData(uid, PowerDeviceVisuals.Powered, powered, appearance); } } diff --git a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs index 048fda2355..51520f0464 100644 --- a/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerReceiverSystem.cs @@ -6,15 +6,18 @@ using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Hands.Components; using Content.Shared.Power; +using Content.Shared.Power.Components; +using Content.Shared.Power.EntitySystems; using Content.Shared.Verbs; using Robust.Server.Audio; using Robust.Server.GameObjects; using Robust.Shared.Audio; +using Robust.Shared.GameStates; using Robust.Shared.Utility; namespace Content.Server.Power.EntitySystems { - public sealed class PowerReceiverSystem : EntitySystem + public sealed class PowerReceiverSystem : SharedPowerReceiverSystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminManager _adminManager = default!; @@ -38,6 +41,8 @@ namespace Content.Server.Power.EntitySystems SubscribeLocalEvent>(OnGetVerbs); SubscribeLocalEvent>(AddSwitchPowerVerb); + SubscribeLocalEvent(OnGetState); + _recQuery = GetEntityQuery(); _provQuery = GetEntityQuery(); } @@ -140,14 +145,18 @@ namespace Content.Server.Power.EntitySystems args.Verbs.Add(verb); } + private void OnGetState(EntityUid uid, ApcPowerReceiverComponent component, ref ComponentGetState args) + { + args.State = new ApcPowerReceiverComponentState + { + Powered = component.Powered + }; + } + private void ProviderChanged(Entity receiver) { var comp = receiver.Comp; comp.NetworkLoad.LinkedNetwork = default; - var ev = new PowerChangedEvent(comp.Powered, comp.NetworkLoad.ReceivingPower); - - RaiseLocalEvent(receiver, ref ev); - _appearance.SetData(receiver, PowerDeviceVisuals.Powered, comp.Powered); } /// @@ -155,12 +164,10 @@ namespace Content.Server.Power.EntitySystems /// Otherwise, it returns 'true' because if something doesn't take power /// it's effectively always powered. /// + /// True when entity has no ApcPowerReceiverComponent or is Powered. False when not. public bool IsPowered(EntityUid uid, ApcPowerReceiverComponent? receiver = null) { - if (!_recQuery.Resolve(uid, ref receiver, false)) - return true; - - return receiver.Powered; + return !_recQuery.Resolve(uid, ref receiver, false) || receiver.Powered; } /// @@ -192,5 +199,10 @@ namespace Content.Server.Power.EntitySystems return !receiver.PowerDisabled; // i.e. PowerEnabled } + + public void SetLoad(ApcPowerReceiverComponent comp, float load) + { + comp.Load = load; + } } } diff --git a/Content.Shared/Power/Components/ApcPowerReceiverComponentState.cs b/Content.Shared/Power/Components/ApcPowerReceiverComponentState.cs new file mode 100644 index 0000000000..9b18d6ad93 --- /dev/null +++ b/Content.Shared/Power/Components/ApcPowerReceiverComponentState.cs @@ -0,0 +1,9 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Power.Components; + +[Serializable, NetSerializable] +public sealed class ApcPowerReceiverComponentState : ComponentState +{ + public bool Powered; +} diff --git a/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs b/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs new file mode 100644 index 0000000000..d73993357a --- /dev/null +++ b/Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Power.Components; + +[NetworkedComponent] +public abstract partial class SharedApcPowerReceiverComponent : Component +{ + [ViewVariables] + public bool Powered; +} diff --git a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs new file mode 100644 index 0000000000..be2a9dc3ab --- /dev/null +++ b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.Power.EntitySystems; + +public abstract class SharedPowerReceiverSystem : EntitySystem +{ + +}