Predict gasthermomachines (#33837)

* Predict gasthermomachines

* despawn

* smellby
This commit is contained in:
metalgearsloth
2025-05-13 21:49:43 +10:00
committed by GitHub
parent 24141aa1e9
commit df2257cd92
16 changed files with 236 additions and 135 deletions

View File

@@ -0,0 +1,29 @@
using Content.Client.Atmos.UI;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Atmos.Piping.Unary.Systems;
namespace Content.Client.Atmos.Piping.Unary.Systems;
public sealed class GasThermoMachineSystem : SharedGasThermoMachineSystem
{
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasThermoMachineComponent, AfterAutoHandleStateEvent>(OnGasAfterState);
}
private void OnGasAfterState(Entity<GasThermoMachineComponent> ent, ref AfterAutoHandleStateEvent args)
{
DirtyUI(ent.Owner, ent.Comp);
}
protected override void DirtyUI(EntityUid uid, GasThermoMachineComponent? thermoMachine, UserInterfaceComponent? ui = null)
{
if (_ui.TryGetOpenUi<GasThermomachineBoundUserInterface>(uid, ThermomachineUiKey.Key, out var bui))
{
bui.Update();
}
}
}

View File

@@ -1,5 +1,8 @@
using Content.Shared.Atmos; using Content.Client.Power.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Atmos.Piping.Unary.Systems;
using Content.Shared.Power.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
@@ -36,6 +39,8 @@ namespace Content.Client.Atmos.UI
_window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed(); _window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed();
_window.TemperatureSpinbox.OnValueChanged += _ => OnTemperatureChanged(_window.TemperatureSpinbox.Value); _window.TemperatureSpinbox.OnValueChanged += _ => OnTemperatureChanged(_window.TemperatureSpinbox.Value);
_window.Entity = Owner;
Update();
} }
private void OnToggleStatusButtonPressed() private void OnToggleStatusButtonPressed()
@@ -43,7 +48,7 @@ namespace Content.Client.Atmos.UI
if (_window is null) return; if (_window is null) return;
_window.SetActive(!_window.Active); _window.SetActive(!_window.Active);
SendMessage(new GasThermomachineToggleMessage()); SendPredictedMessage(new GasThermomachineToggleMessage());
} }
private void OnTemperatureChanged(float value) private void OnTemperatureChanged(float value)
@@ -60,25 +65,32 @@ namespace Content.Client.Atmos.UI
return; return;
} }
SendMessage(new GasThermomachineChangeTemperatureMessage(actual)); SendPredictedMessage(new GasThermomachineChangeTemperatureMessage(actual));
} }
/// <summary> public override void Update()
/// Update the UI state based on server-sent info
/// </summary>
/// <param name="state"></param>
protected override void UpdateState(BoundUserInterfaceState state)
{ {
base.UpdateState(state); if (_window == null || !EntMan.TryGetComponent(Owner, out GasThermoMachineComponent? thermo))
if (_window == null || state is not GasThermomachineBoundUserInterfaceState cast)
return; return;
_minTemp = cast.MinTemperature; var system = EntMan.System<SharedGasThermoMachineSystem>();
_maxTemp = cast.MaxTemperature; _minTemp = thermo.MinTemperature;
_isHeater = cast.IsHeater; _maxTemp = thermo.MaxTemperature;
_isHeater = system.IsHeater(thermo);
_window.SetTemperature(thermo.TargetTemperature);
var receiverSys = EntMan.System<PowerReceiverSystem>();
SharedApcPowerReceiverComponent? receiver = null;
receiverSys.ResolveApc(Owner, ref receiver);
// Also set in frameupdates.
if (receiver != null)
{
_window.SetActive(!receiver.PowerDisabled);
}
_window.SetTemperature(cast.Temperature);
_window.SetActive(cast.Enabled);
_window.Title = _isHeater switch _window.Title = _isHeater switch
{ {
false => Loc.GetString("comp-gas-thermomachine-ui-title-freezer"), false => Loc.GetString("comp-gas-thermomachine-ui-title-freezer"),

View File

@@ -1,5 +1,6 @@
<DefaultWindow xmlns="https://spacestation14.io" <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
MinSize="300 120" Title="{Loc comp-gas-thermomachine-ui-title-freezer}"> MinSize="300 120" Title="{Loc comp-gas-thermomachine-ui-title-freezer}">
<BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10"> <BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
@@ -11,4 +12,4 @@
<Label Text="{Loc comp-gas-thermomachine-ui-temperature}"/> <Label Text="{Loc comp-gas-thermomachine-ui-temperature}"/>
</BoxContainer> </BoxContainer>
</BoxContainer> </BoxContainer>
</DefaultWindow> </controls:FancyWindow>

View File

@@ -1,19 +1,26 @@
using Content.Client.Power.Components;
using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
namespace Content.Client.Atmos.UI; namespace Content.Client.Atmos.UI;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class GasThermomachineWindow : DefaultWindow public sealed partial class GasThermomachineWindow : FancyWindow
{ {
[Dependency] private readonly IEntityManager _entManager = default!;
public bool Active = true; public bool Active = true;
public FloatSpinBox TemperatureSpinbox; public FloatSpinBox TemperatureSpinbox;
public EntityUid Entity;
public GasThermomachineWindow() public GasThermomachineWindow()
{ {
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
SpinboxHBox.AddChild( SpinboxHBox.AddChild(
@@ -27,12 +34,10 @@ public sealed partial class GasThermomachineWindow : DefaultWindow
if (active) if (active)
{ {
ToggleStatusButton.Text = Loc.GetString("comp-gas-thermomachine-ui-status-enabled"); ToggleStatusButton.Text = Loc.GetString("comp-gas-thermomachine-ui-status-enabled");
ToggleStatusButton.Pressed = true;
} }
else else
{ {
ToggleStatusButton.Text = Loc.GetString("comp-gas-thermomachine-ui-status-disabled"); ToggleStatusButton.Text = Loc.GetString("comp-gas-thermomachine-ui-status-disabled");
ToggleStatusButton.Pressed = false;
} }
} }
@@ -40,4 +45,14 @@ public sealed partial class GasThermomachineWindow : DefaultWindow
{ {
TemperatureSpinbox.Value = temperature; TemperatureSpinbox.Value = temperature;
} }
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (_entManager.TryGetComponent(Entity, out ApcPowerReceiverComponent? receiver))
{
SetActive(!receiver.PowerDisabled);
}
}
} }

View File

@@ -0,0 +1,5 @@
using Content.Shared.NodeContainer;
namespace Content.Client.NodeContainer;
public sealed class NodeContainerSystem : SharedNodeContainerSystem;

View File

@@ -1,8 +1,19 @@
using Content.Client.Power.Components;
using Content.Shared.Power.Components;
using Content.Shared.Power.EntitySystems; using Content.Shared.Power.EntitySystems;
namespace Content.Client.Power.EntitySystems; namespace Content.Client.Power.EntitySystems;
public sealed class PowerNetSystem : SharedPowerNetSystem public sealed class PowerNetSystem : SharedPowerNetSystem
{ {
public override bool IsPoweredCalculate(SharedApcPowerReceiverComponent comp)
{
return IsPoweredCalculate((ApcPowerReceiverComponent)comp);
}
private bool IsPoweredCalculate(ApcPowerReceiverComponent comp)
{
return !comp.PowerDisabled
&& !comp.NeedsPower;
}
} }

View File

@@ -9,11 +9,8 @@ using Content.Server.Power.Components;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Shared.UserInterface; using Content.Shared.Atmos.Piping.Unary.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Events; using Content.Shared.DeviceNetwork.Events;
using Content.Shared.Examine; using Content.Shared.Examine;
@@ -22,36 +19,23 @@ using Content.Shared.DeviceNetwork.Components;
namespace Content.Server.Atmos.Piping.Unary.EntitySystems namespace Content.Server.Atmos.Piping.Unary.EntitySystems
{ {
[UsedImplicitly] [UsedImplicitly]
public sealed class GasThermoMachineSystem : EntitySystem public sealed class GasThermoMachineSystem : SharedGasThermoMachineSystem
{ {
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly PowerReceiverSystem _power = default!; [Dependency] private readonly PowerReceiverSystem _power = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNetwork = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetwork = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<GasThermoMachineComponent, AtmosDeviceUpdateEvent>(OnThermoMachineUpdated); SubscribeLocalEvent<GasThermoMachineComponent, AtmosDeviceUpdateEvent>(OnThermoMachineUpdated);
SubscribeLocalEvent<GasThermoMachineComponent, ExaminedEvent>(OnExamined);
// UI events
SubscribeLocalEvent<GasThermoMachineComponent, BeforeActivatableUIOpenEvent>(OnBeforeOpened);
SubscribeLocalEvent<GasThermoMachineComponent, GasThermomachineToggleMessage>(OnToggleMessage);
SubscribeLocalEvent<GasThermoMachineComponent, GasThermomachineChangeTemperatureMessage>(OnChangeTemperature);
// Device network // Device network
SubscribeLocalEvent<GasThermoMachineComponent, DeviceNetworkPacketEvent>(OnPacketRecv); SubscribeLocalEvent<GasThermoMachineComponent, DeviceNetworkPacketEvent>(OnPacketRecv);
} }
private void OnBeforeOpened(Entity<GasThermoMachineComponent> ent, ref BeforeActivatableUIOpenEvent args)
{
DirtyUI(ent, ent.Comp);
}
private void OnThermoMachineUpdated(EntityUid uid, GasThermoMachineComponent thermoMachine, ref AtmosDeviceUpdateEvent args) private void OnThermoMachineUpdated(EntityUid uid, GasThermoMachineComponent thermoMachine, ref AtmosDeviceUpdateEvent args)
{ {
thermoMachine.LastEnergyDelta = 0f; thermoMachine.LastEnergyDelta = 0f;
@@ -135,56 +119,6 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
} }
} }
private bool IsHeater(GasThermoMachineComponent comp)
{
return comp.Cp >= 0;
}
private void OnToggleMessage(EntityUid uid, GasThermoMachineComponent thermoMachine, GasThermomachineToggleMessage args)
{
var powerState = _power.TogglePower(uid);
_adminLogger.Add(LogType.AtmosPowerChanged, $"{ToPrettyString(args.Actor)} turned {(powerState ? "On" : "Off")} {ToPrettyString(uid)}");
DirtyUI(uid, thermoMachine);
}
private void OnChangeTemperature(EntityUid uid, GasThermoMachineComponent thermoMachine, GasThermomachineChangeTemperatureMessage args)
{
if (IsHeater(thermoMachine))
thermoMachine.TargetTemperature = MathF.Min(args.Temperature, thermoMachine.MaxTemperature);
else
thermoMachine.TargetTemperature = MathF.Max(args.Temperature, thermoMachine.MinTemperature);
thermoMachine.TargetTemperature = MathF.Max(thermoMachine.TargetTemperature, Atmospherics.TCMB);
_adminLogger.Add(LogType.AtmosTemperatureChanged, $"{ToPrettyString(args.Actor)} set temperature on {ToPrettyString(uid)} to {thermoMachine.TargetTemperature}");
DirtyUI(uid, thermoMachine);
}
private void DirtyUI(EntityUid uid, GasThermoMachineComponent? thermoMachine, UserInterfaceComponent? ui=null)
{
if (!Resolve(uid, ref thermoMachine, ref ui, false))
return;
ApcPowerReceiverComponent? powerReceiver = null;
if (!Resolve(uid, ref powerReceiver))
return;
_userInterfaceSystem.SetUiState(uid, ThermomachineUiKey.Key,
new GasThermomachineBoundUserInterfaceState(thermoMachine.MinTemperature, thermoMachine.MaxTemperature, thermoMachine.TargetTemperature, !powerReceiver.PowerDisabled, IsHeater(thermoMachine)));
}
private void OnExamined(EntityUid uid, GasThermoMachineComponent thermoMachine, ExaminedEvent args)
{
if (!args.IsInDetailsRange)
return;
if (Loc.TryGetString("gas-thermomachine-system-examined", out var str,
("machineName", !IsHeater(thermoMachine) ? "freezer" : "heater"),
("tempColor", !IsHeater(thermoMachine) ? "deepskyblue" : "red"),
("temp", Math.Round(thermoMachine.TargetTemperature,2))
))
args.PushMarkup(str);
}
private void OnPacketRecv(EntityUid uid, GasThermoMachineComponent component, DeviceNetworkPacketEvent args) private void OnPacketRecv(EntityUid uid, GasThermoMachineComponent component, DeviceNetworkPacketEvent args)
{ {
if (!TryComp(uid, out DeviceNetworkComponent? netConn) if (!TryComp(uid, out DeviceNetworkComponent? netConn)

View File

@@ -5,6 +5,7 @@ using Content.Server.Popups;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Shared.Atmos.Piping.Portable.Components; using Content.Shared.Atmos.Piping.Portable.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Atmos.Visuals; using Content.Shared.Atmos.Visuals;
using Content.Shared.Power; using Content.Shared.Power;
using Content.Shared.UserInterface; using Content.Shared.UserInterface;
@@ -41,6 +42,7 @@ public sealed class SpaceHeaterSystem : EntitySystem
{ {
if (!TryComp<GasThermoMachineComponent>(uid, out var thermoMachine)) if (!TryComp<GasThermoMachineComponent>(uid, out var thermoMachine))
return; return;
thermoMachine.Cp = spaceHeater.HeatingCp; thermoMachine.Cp = spaceHeater.HeatingCp;
thermoMachine.HeatCapacity = spaceHeater.PowerConsumption; thermoMachine.HeatCapacity = spaceHeater.PowerConsumption;
} }

View File

@@ -13,7 +13,7 @@ namespace Content.Server.NodeContainer.EntitySystems
/// </summary> /// </summary>
/// <seealso cref="NodeGroupSystem"/> /// <seealso cref="NodeGroupSystem"/>
[UsedImplicitly] [UsedImplicitly]
public sealed class NodeContainerSystem : EntitySystem public sealed class NodeContainerSystem : SharedNodeContainerSystem
{ {
[Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!; [Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!;
private EntityQuery<NodeContainerComponent> _query; private EntityQuery<NodeContainerComponent> _query;

View File

@@ -31,7 +31,6 @@ namespace Content.Server.Power.EntitySystems
private readonly HashSet<ApcNet> _apcNetReconnectQueue = new(); private readonly HashSet<ApcNet> _apcNetReconnectQueue = new();
private EntityQuery<ApcPowerReceiverBatteryComponent> _apcBatteryQuery; private EntityQuery<ApcPowerReceiverBatteryComponent> _apcBatteryQuery;
private EntityQuery<AppearanceComponent> _appearanceQuery;
private EntityQuery<BatteryComponent> _batteryQuery; private EntityQuery<BatteryComponent> _batteryQuery;
private BatteryRampPegSolver _solver = new(); private BatteryRampPegSolver _solver = new();
@@ -41,7 +40,6 @@ namespace Content.Server.Power.EntitySystems
base.Initialize(); base.Initialize();
_apcBatteryQuery = GetEntityQuery<ApcPowerReceiverBatteryComponent>(); _apcBatteryQuery = GetEntityQuery<ApcPowerReceiverBatteryComponent>();
_appearanceQuery = GetEntityQuery<AppearanceComponent>();
_batteryQuery = GetEntityQuery<BatteryComponent>(); _batteryQuery = GetEntityQuery<BatteryComponent>();
UpdatesAfter.Add(typeof(NodeGroupSystem)); UpdatesAfter.Add(typeof(NodeGroupSystem));
@@ -317,15 +315,25 @@ namespace Content.Server.Power.EntitySystems
_powerNetReconnectQueue.Clear(); _powerNetReconnectQueue.Clear();
} }
private bool IsPoweredCalculate(ApcPowerReceiverComponent comp)
{
return !comp.PowerDisabled
&& (!comp.NeedsPower
|| MathHelper.CloseToPercent(comp.NetworkLoad.ReceivingPower,
comp.Load));
}
public override bool IsPoweredCalculate(SharedApcPowerReceiverComponent comp)
{
return IsPoweredCalculate((ApcPowerReceiverComponent)comp);
}
private void UpdateApcPowerReceiver(float frameTime) private void UpdateApcPowerReceiver(float frameTime)
{ {
var enumerator = AllEntityQuery<ApcPowerReceiverComponent>(); var enumerator = AllEntityQuery<ApcPowerReceiverComponent>();
while (enumerator.MoveNext(out var uid, out var apcReceiver)) while (enumerator.MoveNext(out var uid, out var apcReceiver))
{ {
var powered = !apcReceiver.PowerDisabled var powered = IsPoweredCalculate(apcReceiver);
&& (!apcReceiver.NeedsPower
|| MathHelper.CloseToPercent(apcReceiver.NetworkLoad.ReceivingPower,
apcReceiver.Load));
MetaDataComponent? metadata = null; MetaDataComponent? metadata = null;
@@ -381,9 +389,6 @@ namespace Content.Server.Power.EntitySystems
var ev = new PowerChangedEvent(powered, apcReceiver.NetworkLoad.ReceivingPower); var ev = new PowerChangedEvent(powered, apcReceiver.NetworkLoad.ReceivingPower);
RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(uid, ref ev);
if (_appearanceQuery.TryComp(uid, out var appearance))
_appearance.SetData(uid, PowerDeviceVisuals.Powered, powered, appearance);
} }
} }

View File

@@ -1,9 +1,10 @@
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Guidebook; using Content.Shared.Guidebook;
using Robust.Shared.GameStates;
namespace Content.Server.Atmos.Piping.Unary.Components namespace Content.Shared.Atmos.Piping.Unary.Components
{ {
[RegisterComponent] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
public sealed partial class GasThermoMachineComponent : Component public sealed partial class GasThermoMachineComponent : Component
{ {
[DataField("inlet")] [DataField("inlet")]
@@ -13,11 +14,11 @@ namespace Content.Server.Atmos.Piping.Unary.Components
/// Current electrical power consumption, in watts. Increasing power increases the ability of the /// Current electrical power consumption, in watts. Increasing power increases the ability of the
/// thermomachine to heat or cool air. /// thermomachine to heat or cool air.
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField]
[GuidebookData] [GuidebookData]
public float HeatCapacity = 5000; public float HeatCapacity = 5000;
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField, AutoNetworkedField]
public float TargetTemperature = Atmospherics.T20C; public float TargetTemperature = Atmospherics.T20C;
/// <summary> /// <summary>
@@ -39,42 +40,41 @@ namespace Content.Server.Atmos.Piping.Unary.Components
/// Positive for heaters, negative for freezers. /// Positive for heaters, negative for freezers.
/// </summary> /// </summary>
[DataField("coefficientOfPerformance")] [DataField("coefficientOfPerformance")]
[ViewVariables(VVAccess.ReadWrite)]
public float Cp = 0.9f; // output power / input power, positive is heat public float Cp = 0.9f; // output power / input power, positive is heat
/// <summary> /// <summary>
/// Current minimum temperature /// Current minimum temperature
/// Ignored if heater. /// Ignored if heater.
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField, AutoNetworkedField]
[GuidebookData] [GuidebookData]
public float MinTemperature = 73.15f; public float MinTemperature = 73.15f;
/// <summary> /// <summary>
/// Current maximum temperature /// Current maximum temperature
/// Ignored if freezer. /// Ignored if freezer.
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField, AutoNetworkedField]
[GuidebookData] [GuidebookData]
public float MaxTemperature = 593.15f; public float MaxTemperature = 593.15f;
/// <summary> /// <summary>
/// Last amount of energy added/removed from the attached pipe network /// Last amount of energy added/removed from the attached pipe network
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField]
public float LastEnergyDelta; public float LastEnergyDelta;
/// <summary> /// <summary>
/// An percentage of the energy change that is leaked into the surrounding environment rather than the inlet pipe. /// An percentage of the energy change that is leaked into the surrounding environment rather than the inlet pipe.
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField]
[GuidebookData] [GuidebookData]
public float EnergyLeakPercentage; public float EnergyLeakPercentage;
/// <summary> /// <summary>
/// If true, heat is exclusively exchanged with the local atmosphere instead of the inlet pipe air /// If true, heat is exclusively exchanged with the local atmosphere instead of the inlet pipe air
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField]
public bool Atmospheric = false; public bool Atmospheric;
} }
} }

View File

@@ -7,7 +7,7 @@ public sealed record GasThermoMachineData(float EnergyDelta);
[Serializable] [Serializable]
[NetSerializable] [NetSerializable]
public enum ThermomachineUiKey public enum ThermomachineUiKey : byte
{ {
Key Key
} }
@@ -29,23 +29,3 @@ public sealed class GasThermomachineChangeTemperatureMessage : BoundUserInterfac
Temperature = temperature; Temperature = temperature;
} }
} }
[Serializable]
[NetSerializable]
public sealed class GasThermomachineBoundUserInterfaceState : BoundUserInterfaceState
{
public float MinTemperature { get; }
public float MaxTemperature { get; }
public float Temperature { get; }
public bool Enabled { get; }
public bool IsHeater { get; }
public GasThermomachineBoundUserInterfaceState(float minTemperature, float maxTemperature, float temperature, bool enabled, bool isHeater)
{
MinTemperature = minTemperature;
MaxTemperature = maxTemperature;
Temperature = temperature;
Enabled = enabled;
IsHeater = isHeater;
}
}

View File

@@ -0,0 +1,61 @@
using Content.Shared.Administration.Logs;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Database;
using Content.Shared.Examine;
using Content.Shared.Power.EntitySystems;
namespace Content.Shared.Atmos.Piping.Unary.Systems;
public abstract class SharedGasThermoMachineSystem : EntitySystem
{
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedPowerReceiverSystem _receiver = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasThermoMachineComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<GasThermoMachineComponent, GasThermomachineToggleMessage>(OnToggleMessage);
SubscribeLocalEvent<GasThermoMachineComponent, GasThermomachineChangeTemperatureMessage>(OnChangeTemperature);
}
private void OnExamined(EntityUid uid, GasThermoMachineComponent thermoMachine, ExaminedEvent args)
{
if (Loc.TryGetString("gas-thermomachine-system-examined",
out var str,
("machineName", !IsHeater(thermoMachine) ? "freezer" : "heater"),
("tempColor", !IsHeater(thermoMachine) ? "deepskyblue" : "red"),
("temp", Math.Round(thermoMachine.TargetTemperature, 2))
))
{
args.PushMarkup(str);
}
}
public bool IsHeater(GasThermoMachineComponent comp)
{
return comp.Cp >= 0;
}
private void OnToggleMessage(EntityUid uid, GasThermoMachineComponent thermoMachine, GasThermomachineToggleMessage args)
{
var powerState = _receiver.TogglePower(uid, user: args.Actor);
_adminLogger.Add(LogType.AtmosPowerChanged, $"{ToPrettyString(args.Actor)} turned {(powerState ? "On" : "Off")} {ToPrettyString(uid)}");
DirtyUI(uid, thermoMachine);
}
private void OnChangeTemperature(EntityUid uid, GasThermoMachineComponent thermoMachine, GasThermomachineChangeTemperatureMessage args)
{
if (IsHeater(thermoMachine))
thermoMachine.TargetTemperature = MathF.Min(args.Temperature, thermoMachine.MaxTemperature);
else
thermoMachine.TargetTemperature = MathF.Max(args.Temperature, thermoMachine.MinTemperature);
thermoMachine.TargetTemperature = MathF.Max(thermoMachine.TargetTemperature, Atmospherics.TCMB);
_adminLogger.Add(LogType.AtmosTemperatureChanged, $"{ToPrettyString(args.Actor)} set temperature on {ToPrettyString(uid)} to {thermoMachine.TargetTemperature}");
Dirty(uid, thermoMachine);
DirtyUI(uid, thermoMachine);
}
protected virtual void DirtyUI(EntityUid uid, GasThermoMachineComponent? thermoMachine, UserInterfaceComponent? ui=null) {}
}

View File

@@ -0,0 +1,6 @@
namespace Content.Shared.NodeContainer;
public abstract class SharedNodeContainerSystem : EntitySystem
{
}

View File

@@ -1,6 +1,21 @@
using Content.Shared.Power.Components;
namespace Content.Shared.Power.EntitySystems; namespace Content.Shared.Power.EntitySystems;
public abstract class SharedPowerNetSystem : EntitySystem public abstract class SharedPowerNetSystem : EntitySystem
{ {
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
public abstract bool IsPoweredCalculate(SharedApcPowerReceiverComponent comp);
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AppearanceComponent, PowerChangedEvent>(OnPowerAppearance);
}
private void OnPowerAppearance(Entity<AppearanceComponent> ent, ref PowerChangedEvent args)
{
_appearance.SetData(ent, PowerDeviceVisuals.Powered, args.Powered, ent.Comp);
}
} }

View File

@@ -4,13 +4,16 @@ using Content.Shared.Database;
using Content.Shared.Power.Components; using Content.Shared.Power.Components;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
namespace Content.Shared.Power.EntitySystems; namespace Content.Shared.Power.EntitySystems;
public abstract class SharedPowerReceiverSystem : EntitySystem public abstract class SharedPowerReceiverSystem : EntitySystem
{ {
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPowerNetSystem _net = default!;
public abstract bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component); public abstract bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component);
@@ -44,6 +47,15 @@ public abstract class SharedPowerReceiverSystem : EntitySystem
// it'll save a lot of confusion if 'always powered' means 'always powered' // it'll save a lot of confusion if 'always powered' means 'always powered'
if (!receiver.NeedsPower) if (!receiver.NeedsPower)
{ {
var powered = _net.IsPoweredCalculate(receiver);
// Server won't raise it here as it can raise the load event later with NeedsPower?
// This is mostly here for clientside predictions.
if (receiver.Powered != powered)
{
RaisePower((uid, receiver));
}
SetPowerDisabled(uid, false, receiver); SetPowerDisabled(uid, false, receiver);
return true; return true;
} }
@@ -59,6 +71,19 @@ public abstract class SharedPowerReceiverSystem : EntitySystem
AudioParams.Default.WithVolume(-2f)); AudioParams.Default.WithVolume(-2f));
} }
if (_netMan.IsClient && receiver.PowerDisabled)
{
var powered = _net.IsPoweredCalculate(receiver);
// Server won't raise it here as it can raise the load event later with NeedsPower?
// This is mostly here for clientside predictions.
if (receiver.Powered != powered)
{
receiver.Powered = powered;
RaisePower((uid, receiver));
}
}
return !receiver.PowerDisabled; // i.e. PowerEnabled return !receiver.PowerDisabled; // i.e. PowerEnabled
} }