]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Predict dumping (#32394)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Sat, 19 Apr 2025 06:20:40 +0000 (16:20 +1000)
committerGitHub <noreply@github.com>
Sat, 19 Apr 2025 06:20:40 +0000 (16:20 +1000)
* Predict dumping

- This got soaped really fucking hard.
- Dumping is predicted, this required disposals to be predicte.d
- Disposals required mailing (because it's tightly coupled), and a smidge of other content systems.
- I also had to fix a compnetworkgenerator issue at the same time so it wouldn't mispredict.

* Fix a bunch of stuff

* nasty merge

* Some reviews

* Some more reviews while I stash

* Fix merge

* Fix merge

* Half of review

* Review

* re(h)f

* lizards

* feexes

* feex

140 files changed:
Content.Client/Configurable/ConfigurationSystem.cs [new file with mode: 0644]
Content.Client/Configurable/UI/ConfigurationBoundUserInterface.cs
Content.Client/Configurable/UI/ConfigurationMenu.cs
Content.Client/Decals/DecalSystem.cs
Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs [new file with mode: 0644]
Content.Client/Disposal/DisposalUnitComponent.cs [deleted file]
Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs [new file with mode: 0644]
Content.Client/Disposal/Mailing/MailingUnitSystem.cs [new file with mode: 0644]
Content.Client/Disposal/Mailing/MailingUnitWindow.xaml [moved from Content.Client/Disposal/UI/MailingUnitWindow.xaml with 80% similarity]
Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs [new file with mode: 0644]
Content.Client/Disposal/PressureBar.cs [moved from Content.Client/Disposal/UI/PressureBar.cs with 96% similarity]
Content.Client/Disposal/Systems/DisposalUnitSystem.cs [deleted file]
Content.Client/Disposal/Tube/DisposalRouterBoundUserInterface.cs [moved from Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs with 95% similarity]
Content.Client/Disposal/Tube/DisposalRouterWindow.xaml [moved from Content.Client/Disposal/UI/DisposalRouterWindow.xaml with 100% similarity]
Content.Client/Disposal/Tube/DisposalRouterWindow.xaml.cs [moved from Content.Client/Disposal/UI/DisposalRouterWindow.xaml.cs with 90% similarity]
Content.Client/Disposal/Tube/DisposalTaggerBoundUserInterface.cs [moved from Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs with 95% similarity]
Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml [moved from Content.Client/Disposal/UI/DisposalTaggerWindow.xaml with 100% similarity]
Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml.cs [moved from Content.Client/Disposal/UI/DisposalTaggerWindow.xaml.cs with 90% similarity]
Content.Client/Disposal/Tube/DisposalTubeSystem.cs [new file with mode: 0644]
Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs [deleted file]
Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs [deleted file]
Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs [deleted file]
Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs [new file with mode: 0644]
Content.Client/Disposal/Unit/DisposalUnitSystem.cs [new file with mode: 0644]
Content.Client/Disposal/Unit/DisposalUnitWindow.xaml [moved from Content.Client/Disposal/UI/DisposalUnitWindow.xaml with 72% similarity]
Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs [new file with mode: 0644]
Content.Client/NetworkConfigurator/Systems/DeviceListSystem.cs
Content.Client/Power/EntitySystems/PowerReceiverSystem.cs
Content.Client/Storage/Visualizers/StorageFillVisualizerSystem.cs
Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs
Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTestSystem.cs
Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs
Content.Server/Administration/Systems/AdminVerbSystem.cs
Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs
Content.Server/Atmos/Consoles/AtmosMonitoringConsoleSystem.cs
Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs
Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs
Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs
Content.Server/Atmos/Monitor/Systems/FireAlarmSystem.cs
Content.Server/Atmos/Monitor/WireActions/AirAlarmPanicWire.cs
Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs
Content.Server/Atmos/Piping/Unary/Components/GasOutletInjectorComponent.cs
Content.Server/Atmos/Piping/Unary/EntitySystems/GasThermoMachineSystem.cs
Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs
Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs
Content.Server/CartridgeLoader/CartridgeLoaderSystem.cs
Content.Server/CartridgeLoader/Cartridges/NetProbeCartridgeSystem.cs
Content.Server/Communications/CommunicationsConsoleSystem.cs
Content.Server/Configurable/ConfigurationSystem.cs
Content.Server/Containers/ThrowInsertContainerSystem.cs
Content.Server/DeviceLinking/Systems/DeviceLinkSystem.cs
Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs
Content.Server/DeviceLinking/Systems/EdgeDetectorSystem.cs
Content.Server/DeviceLinking/Systems/LogicGateSystem.cs
Content.Server/DeviceLinking/Systems/MemoryCellSystem.cs
Content.Server/DeviceNetwork/Systems/ApcNetworkSystem.cs
Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs
Content.Server/DeviceNetwork/Systems/DeviceNetworkJammerSystem.cs
Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs
Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs
Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs
Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs
Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs
Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs
Content.Server/DeviceNetwork/Systems/WiredNetworkSystem.cs
Content.Server/DeviceNetwork/Systems/WirelessNetworkSystem.cs
Content.Server/Disposal/Mailing/MailingUnitSystem.cs
Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs [deleted file]
Content.Server/Disposal/Tube/DisposalBendComponent.cs [moved from Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs with 70% similarity]
Content.Server/Disposal/Tube/DisposalJunctionComponent.cs [moved from Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs with 84% similarity]
Content.Server/Disposal/Tube/DisposalRouterComponent.cs [moved from Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs with 57% similarity]
Content.Server/Disposal/Tube/DisposalSignalRouterComponent.cs [moved from Content.Server/Disposal/Tube/Components/DisposalSignalRouterComponent.cs with 91% similarity]
Content.Server/Disposal/Tube/DisposalSignalRouterSystem.cs [moved from Content.Server/Disposal/Tube/Systems/DisposalSignalRouterSystem.cs with 94% similarity]
Content.Server/Disposal/Tube/DisposalTaggerComponent.cs [moved from Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs with 53% similarity]
Content.Server/Disposal/Tube/DisposalTransitComponent.cs [moved from Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs with 82% similarity]
Content.Server/Disposal/Tube/DisposalTubeComponent.cs [moved from Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs with 90% similarity]
Content.Server/Disposal/Tube/DisposalTubeSystem.cs
Content.Server/Disposal/Tube/GetDisposalsNextDirectionEvent.cs
Content.Server/Disposal/TubeConnectionsCommand.cs
Content.Server/Disposal/Unit/BeingDisposedComponent.cs [moved from Content.Server/Disposal/Unit/Components/BeingDisposedComponent.cs with 81% similarity]
Content.Server/Disposal/Unit/BeingDisposedSystem.cs [moved from Content.Server/Disposal/Unit/EntitySystems/BeingDisposedSystem.cs with 92% similarity]
Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs [deleted file]
Content.Server/Disposal/Unit/DisposableSystem.cs [moved from Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs with 98% similarity]
Content.Server/Disposal/Unit/DisposalHolderComponent.cs [moved from Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs with 94% similarity]
Content.Server/Disposal/Unit/DisposalUnitSystem.cs [new file with mode: 0644]
Content.Server/Disposal/Unit/DoInsertDisposalUnitEvent.cs [moved from Content.Server/Disposal/Unit/EntitySystems/DoInsertDisposalUnitEvent.cs with 65% similarity]
Content.Server/Fax/AdminUI/AdminFaxEui.cs
Content.Server/Fax/FaxSystem.cs
Content.Server/Light/EntitySystems/PoweredLightSystem.cs
Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs
Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs
Content.Server/Medical/SuitSensors/SuitSensorSystem.cs
Content.Server/PDA/PdaSystem.cs
Content.Server/Power/EntitySystems/PowerReceiverSystem.cs
Content.Server/Power/Generation/Teg/TegSystem.cs
Content.Server/Radio/EntitySystems/JammerSystem.cs
Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs
Content.Server/RoundEnd/RoundEndSystem.cs
Content.Server/Screens/Systems/ScreenSystem.cs
Content.Server/SensorMonitoring/BatterySensorSystem.cs
Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.UI.cs
Content.Server/SensorMonitoring/SensorMonitoringConsoleSystem.cs
Content.Server/Shuttles/Systems/ArrivalsSystem.cs
Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs
Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs
Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs
Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs
Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs
Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs
Content.Server/Turrets/DeployableTurretSystem.cs
Content.Shared/Climbing/Components/ClimbableComponent.cs
Content.Shared/Climbing/Systems/ClimbSystem.cs
Content.Shared/Configurable/ConfigurationComponent.cs
Content.Shared/Configurable/SharedConfigurationSystem.cs [new file with mode: 0644]
Content.Shared/Containers/SharedThrowInsertContainerSystem.cs [new file with mode: 0644]
Content.Shared/DeviceNetwork/Components/DeviceNetworkComponent.cs [moved from Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs with 93% similarity]
Content.Shared/DeviceNetwork/DeviceNet.cs [moved from Content.Server/DeviceNetwork/DeviceNet.cs with 97% similarity]
Content.Shared/DeviceNetwork/DeviceNetworkConstants.cs [moved from Content.Server/DeviceNetwork/DeviceNetworkConstants.cs with 96% similarity]
Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs [new file with mode: 0644]
Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs [new file with mode: 0644]
Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs [new file with mode: 0644]
Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs [new file with mode: 0644]
Content.Shared/Disposal/Mailing/MailingUnitComponent.cs [moved from Content.Server/Disposal/Mailing/MailingUnitComponent.cs with 57% similarity]
Content.Shared/Disposal/Mailing/MailingUnitUiMessages.cs [moved from Content.Shared/Disposal/MailingUnitUiMessages.cs with 100% similarity]
Content.Shared/Disposal/Mailing/SharedDisposalRouterComponent.cs [moved from Content.Shared/Disposal/Components/SharedDisposalRouterComponent.cs with 100% similarity]
Content.Shared/Disposal/Mailing/SharedDisposalTaggerComponent.cs [moved from Content.Shared/Disposal/Components/SharedDisposalTaggerComponent.cs with 100% similarity]
Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs [new file with mode: 0644]
Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs [deleted file]
Content.Shared/Disposal/SharedDisposalUnitSystem.cs [deleted file]
Content.Shared/Disposal/Tube/DisposalEntryComponent.cs [new file with mode: 0644]
Content.Shared/Disposal/Tube/SharedDisposalTubeComponent.cs [moved from Content.Shared/Disposal/Components/SharedDisposalTubeComponent.cs with 100% similarity]
Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs [new file with mode: 0644]
Content.Shared/Disposal/Unit/DisposalUnitComponent.cs [moved from Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs with 68% similarity]
Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs [new file with mode: 0644]
Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs [moved from Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs with 53% similarity]
Content.Shared/Power/Components/SharedApcPowerReceiverComponent.cs
Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs
Content.Shared/Storage/EntitySystems/DumpableSystem.cs
Resources/Prototypes/Entities/Structures/Furniture/toilet.yml
Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml

diff --git a/Content.Client/Configurable/ConfigurationSystem.cs b/Content.Client/Configurable/ConfigurationSystem.cs
new file mode 100644 (file)
index 0000000..6594375
--- /dev/null
@@ -0,0 +1,25 @@
+using Content.Client.Configurable.UI;
+using Content.Shared.Configurable;
+
+namespace Content.Client.Configurable;
+
+public sealed class ConfigurationSystem : SharedConfigurationSystem
+{
+    [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<ConfigurationComponent, AfterAutoHandleStateEvent>(OnConfigurationState);
+    }
+
+    private void OnConfigurationState(Entity<ConfigurationComponent> ent, ref AfterAutoHandleStateEvent args)
+    {
+        if (_uiSystem.TryGetOpenUi<ConfigurationBoundUserInterface>(ent.Owner,
+                ConfigurationComponent.ConfigurationUiKey.Key,
+                out var bui))
+        {
+            bui.Refresh(ent);
+        }
+    }
+}
index e4966f1ec43cf6fe7dccd92239b51638d16bb5cd..a3845ba1eb4416fe06ca7b38c9cab93edb8a925e 100644 (file)
@@ -1,6 +1,8 @@
-using System.Text.RegularExpressions;
-using Robust.Client.GameObjects;
+using System.Numerics;
+using System.Text.RegularExpressions;
+using Content.Shared.Configurable;
 using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
 using static Content.Shared.Configurable.ConfigurationComponent;
 
 namespace Content.Client.Configurable.UI
@@ -19,16 +21,53 @@ namespace Content.Client.Configurable.UI
             base.Open();
             _menu = this.CreateWindow<ConfigurationMenu>();
             _menu.OnConfiguration += SendConfiguration;
+            if (EntMan.TryGetComponent(Owner, out ConfigurationComponent? component))
+                Refresh((Owner, component));
         }
 
-        protected override void UpdateState(BoundUserInterfaceState state)
+        public void Refresh(Entity<ConfigurationComponent> entity)
         {
-            base.UpdateState(state);
-
-            if (state is not ConfigurationBoundUserInterfaceState configurationState)
+            if (_menu == null)
                 return;
 
-            _menu?.Populate(configurationState);
+            _menu.Column.Children.Clear();
+            _menu.Inputs.Clear();
+
+            foreach (var field in entity.Comp.Config)
+            {
+                var label = new Label
+                {
+                    Margin = new Thickness(0, 0, 8, 0),
+                    Name = field.Key,
+                    Text = field.Key + ":",
+                    VerticalAlignment = Control.VAlignment.Center,
+                    HorizontalExpand = true,
+                    SizeFlagsStretchRatio = .2f,
+                    MinSize = new Vector2(60, 0)
+                };
+
+                var input = new LineEdit
+                {
+                    Name = field.Key + "-input",
+                    Text = field.Value ?? "",
+                    IsValid = _menu.Validate,
+                    HorizontalExpand = true,
+                    SizeFlagsStretchRatio = .8f
+                };
+
+                _menu.Inputs.Add((field.Key, input));
+
+                var row = new BoxContainer
+                {
+                    Orientation = BoxContainer.LayoutOrientation.Horizontal
+                };
+
+                ConfigurationMenu.CopyProperties(_menu.Row, row);
+
+                row.AddChild(label);
+                row.AddChild(input);
+                _menu.Column.AddChild(row);
+            }
         }
 
         protected override void ReceiveMessage(BoundUserInterfaceMessage message)
index 29217eef7be159e371dfc690146d2eae48055ee5..4ca68c5fa0da659963aa389f49fbf1b406a4d1dc 100644 (file)
@@ -1,12 +1,8 @@
-using System.Collections.Generic;
-using System.Numerics;
+using System.Numerics;
 using System.Text.RegularExpressions;
 using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.CustomControls;
-using Robust.Shared.Localization;
-using Robust.Shared.Maths;
-using static Content.Shared.Configurable.ConfigurationComponent;
 using static Robust.Client.UserInterface.Controls.BaseButton;
 using static Robust.Client.UserInterface.Controls.BoxContainer;
 
@@ -14,10 +10,10 @@ namespace Content.Client.Configurable.UI
 {
     public sealed class ConfigurationMenu : DefaultWindow
     {
-        private readonly BoxContainer _column;
-        private readonly BoxContainer _row;
+        public readonly BoxContainer Column;
+        public readonly BoxContainer Row;
 
-        private readonly List<(string  name, LineEdit input)> _inputs;
+        public readonly List<(string name, LineEdit input)> Inputs;
 
         [ViewVariables]
         public Regex? Validation { get; internal set; }
@@ -28,7 +24,7 @@ namespace Content.Client.Configurable.UI
         {
             MinSize = SetSize = new Vector2(300, 250);
 
-            _inputs = new List<(string name, LineEdit input)>();
+            Inputs = new List<(string name, LineEdit input)>();
 
             Title = Loc.GetString("configuration-menu-device-title");
 
@@ -39,14 +35,14 @@ namespace Content.Client.Configurable.UI
                 HorizontalExpand = true
             };
 
-            _column = new BoxContainer
+            Column = new BoxContainer
             {
                 Orientation = LayoutOrientation.Vertical,
                 Margin = new Thickness(8),
                 SeparationOverride = 16,
             };
 
-            _row = new BoxContainer
+            Row = new BoxContainer
             {
                 Orientation = LayoutOrientation.Horizontal,
                 SeparationOverride = 16,
@@ -69,61 +65,20 @@ namespace Content.Client.Configurable.UI
                 ModulateSelfOverride = Color.FromHex("#202025")
             };
 
-            outerColumn.AddChild(_column);
+            outerColumn.AddChild(Column);
             baseContainer.AddChild(outerColumn);
             baseContainer.AddChild(confirmButton);
             Contents.AddChild(baseContainer);
         }
 
-        public void Populate(ConfigurationBoundUserInterfaceState state)
-        {
-            _column.Children.Clear();
-            _inputs.Clear();
-
-            foreach (var field in state.Config)
-            {
-                var label = new Label
-                {
-                    Margin = new Thickness(0, 0, 8, 0),
-                    Name = field.Key,
-                    Text = field.Key + ":",
-                    VerticalAlignment = VAlignment.Center,
-                    HorizontalExpand = true,
-                    SizeFlagsStretchRatio = .2f,
-                    MinSize = new Vector2(60, 0)
-                };
-
-                var input = new LineEdit
-                {
-                    Name = field.Key + "-input",
-                    Text = field.Value ?? "",
-                    IsValid = Validate,
-                    HorizontalExpand = true,
-                    SizeFlagsStretchRatio = .8f
-                };
-
-                _inputs.Add((field.Key, input));
-
-                var row = new BoxContainer
-                {
-                    Orientation = LayoutOrientation.Horizontal
-                };
-                CopyProperties(_row, row);
-
-                row.AddChild(label);
-                row.AddChild(input);
-                _column.AddChild(row);
-            }
-        }
-
         private void OnConfirm(ButtonEventArgs args)
         {
-            var config = GenerateDictionary(_inputs, "Text");
+            var config = GenerateDictionary(Inputs, "Text");
             OnConfiguration?.Invoke(config);
             Close();
         }
 
-        private bool Validate(string value)
+        public bool Validate(string value)
         {
             return Validation?.IsMatch(value) != false;
         }
@@ -140,7 +95,7 @@ namespace Content.Client.Configurable.UI
             return dictionary;
         }
 
-        private static void CopyProperties<T>(T from, T to) where T : Control
+        public static void CopyProperties<T>(T from, T to) where T : Control
         {
             foreach (var property in from.AllAttachedProperties)
             {
index 41e5f39c2867f75eb5a3c1cc50a946c7a7c649cb..172a06c4cde5e809e198b148118aeecfcf08f808 100644 (file)
@@ -13,7 +13,7 @@ namespace Content.Client.Decals
         [Dependency] private readonly IOverlayManager _overlayManager = default!;
         [Dependency] private readonly SpriteSystem _sprites = default!;
 
-        private DecalOverlay _overlay = default!;
+        private DecalOverlay? _overlay;
 
         private HashSet<uint> _removedUids = new();
         private readonly List<Vector2i> _removedChunks = new();
@@ -31,6 +31,9 @@ namespace Content.Client.Decals
 
         public void ToggleOverlay()
         {
+            if (_overlay == null)
+                return;
+
             if (_overlayManager.HasOverlay<DecalOverlay>())
             {
                 _overlayManager.RemoveOverlay(_overlay);
@@ -44,6 +47,10 @@ namespace Content.Client.Decals
         public override void Shutdown()
         {
             base.Shutdown();
+
+            if (_overlay == null)
+                return;
+
             _overlayManager.RemoveOverlay(_overlay);
         }
 
diff --git a/Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs b/Content.Client/DeviceNetwork/Systems/DeviceNetworkSystem.cs
new file mode 100644 (file)
index 0000000..5b11b2b
--- /dev/null
@@ -0,0 +1,8 @@
+using Content.Shared.DeviceNetwork.Systems;
+
+namespace Content.Client.DeviceNetwork.Systems;
+
+public sealed class DeviceNetworkSystem : SharedDeviceNetworkSystem
+{
+
+}
diff --git a/Content.Client/Disposal/DisposalUnitComponent.cs b/Content.Client/Disposal/DisposalUnitComponent.cs
deleted file mode 100644 (file)
index e63a3fd..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-using Content.Shared.Disposal.Components;
-
-namespace Content.Client.Disposal;
-
-[RegisterComponent]
-public sealed partial class DisposalUnitComponent : SharedDisposalUnitComponent
-{
-
-}
diff --git a/Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs b/Content.Client/Disposal/Mailing/MailingUnitBoundUserInterface.cs
new file mode 100644 (file)
index 0000000..013c4ea
--- /dev/null
@@ -0,0 +1,79 @@
+using Content.Client.Disposal.Unit;
+using Content.Client.Power.EntitySystems;
+using Content.Shared.Disposal;
+using Content.Shared.Disposal.Components;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+
+namespace Content.Client.Disposal.Mailing;
+
+public sealed class MailingUnitBoundUserInterface : BoundUserInterface
+{
+    [ViewVariables]
+    public MailingUnitWindow? MailingUnitWindow;
+
+    public MailingUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+    {
+    }
+
+    private void ButtonPressed(DisposalUnitComponent.UiButton button)
+    {
+        SendMessage(new DisposalUnitComponent.UiButtonPressedMessage(button));
+        // If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs
+        // the pressure lerp up.
+    }
+
+    private void TargetSelected(ItemList.ItemListSelectedEventArgs args)
+    {
+        var item = args.ItemList[args.ItemIndex];
+        SendMessage(new TargetSelectedMessage(item.Text));
+    }
+
+    protected override void Open()
+    {
+        base.Open();
+
+        MailingUnitWindow = this.CreateWindow<MailingUnitWindow>();
+        MailingUnitWindow.OpenCenteredRight();
+
+        MailingUnitWindow.Eject.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Eject);
+        MailingUnitWindow.Engage.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Engage);
+        MailingUnitWindow.Power.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Power);
+
+        MailingUnitWindow.TargetListContainer.OnItemSelected += TargetSelected;
+
+        if (EntMan.TryGetComponent(Owner, out MailingUnitComponent? component))
+            Refresh((Owner, component));
+    }
+
+    public void Refresh(Entity<MailingUnitComponent> entity)
+    {
+        if (MailingUnitWindow == null)
+            return;
+
+        // TODO: This should be decoupled from disposals
+        if (EntMan.TryGetComponent(entity.Owner, out DisposalUnitComponent? disposals))
+        {
+            var disposalSystem = EntMan.System<DisposalUnitSystem>();
+
+            var disposalState = disposalSystem.GetState(Owner, disposals);
+            var fullPressure = disposalSystem.EstimatedFullPressure(Owner, disposals);
+
+            MailingUnitWindow.UnitState.Text = Loc.GetString($"disposal-unit-state-{disposalState}");
+            MailingUnitWindow.FullPressure = fullPressure;
+            MailingUnitWindow.PressureBar.UpdatePressure(fullPressure);
+            MailingUnitWindow.Power.Pressed = EntMan.System<PowerReceiverSystem>().IsPowered(Owner);
+            MailingUnitWindow.Engage.Pressed = disposals.Engaged;
+        }
+
+        MailingUnitWindow.Title = Loc.GetString("ui-mailing-unit-window-title", ("tag", entity.Comp.Tag ?? " "));
+        //UnitTag.Text = state.Tag;
+        MailingUnitWindow.Target.Text = entity.Comp.Target;
+
+        MailingUnitWindow.TargetListContainer.Clear();
+        foreach (var target in entity.Comp.TargetList)
+        {
+            MailingUnitWindow.TargetListContainer.AddItem(target);
+        }
+    }
+}
diff --git a/Content.Client/Disposal/Mailing/MailingUnitSystem.cs b/Content.Client/Disposal/Mailing/MailingUnitSystem.cs
new file mode 100644 (file)
index 0000000..780656b
--- /dev/null
@@ -0,0 +1,22 @@
+using Content.Shared.Disposal;
+using Content.Shared.Disposal.Components;
+using Content.Shared.Disposal.Mailing;
+
+namespace Content.Client.Disposal.Mailing;
+
+public sealed class MailingUnitSystem : SharedMailingUnitSystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<MailingUnitComponent, AfterAutoHandleStateEvent>(OnMailingState);
+    }
+
+    private void OnMailingState(Entity<MailingUnitComponent> ent, ref AfterAutoHandleStateEvent args)
+    {
+        if (UserInterfaceSystem.TryGetOpenUi<MailingUnitBoundUserInterface>(ent.Owner, MailingUnitUiKey.Key, out var bui))
+        {
+            bui.Refresh(ent);
+        }
+    }
+}
similarity index 80%
rename from Content.Client/Disposal/UI/MailingUnitWindow.xaml
rename to Content.Client/Disposal/Mailing/MailingUnitWindow.xaml
index c57ca7b4c135904f27115aec296ff7fd1798d9cc..0acd300895278a26533010574eb23bf3a1be3935 100644 (file)
@@ -1,12 +1,14 @@
-<DefaultWindow xmlns="https://spacestation14.io"
-               xmlns:ui="clr-namespace:Content.Client.Disposal.UI"
-               MinSize="300 400"
+<controls:FancyWindow xmlns="https://spacestation14.io"
+                      xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
+                      xmlns:disposal="clr-namespace:Content.Client.Disposal"
+                      MinSize="300 400"
             SetSize="300 400"
             Resizable="False">
     <BoxContainer Orientation="Vertical">
         <BoxContainer Orientation="Horizontal" SeparationOverride="8">
             <Label Text="{Loc 'ui-mailing-unit-target-label'}" />
             <Label Name="Target"
+                   Access="Public"
                    Text="" />
         </BoxContainer>
         <ItemList Name="TargetListContainer"
         </ItemList>
         <BoxContainer Orientation="Horizontal" SeparationOverride="4">
             <Label Text="{Loc 'ui-disposal-unit-label-state'}" />
-            <Label Name="UnitState"
+            <Label Name="UnitState" Access="Public"
                    Text="{Loc 'ui-disposal-unit-label-status'}" />
         </BoxContainer>
         <Control MinSize="0 5" />
         <BoxContainer Orientation="Horizontal"
                       SeparationOverride="4">
             <Label Text="{Loc 'ui-disposal-unit-label-pressure'}" />
-            <ui:PressureBar Name="PressureBar"
+            <disposal:PressureBar Name="PressureBar"
+                            Access="Public"
                          MinSize="190 20"
                          HorizontalAlignment="Right"
                          MinValue="0"
@@ -50,4 +53,4 @@
                          StyleClasses="OpenLeft" />
         </BoxContainer>
     </BoxContainer>
-</DefaultWindow>
+</controls:FancyWindow>
diff --git a/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs b/Content.Client/Disposal/Mailing/MailingUnitWindow.xaml.cs
new file mode 100644 (file)
index 0000000..8f1dcaf
--- /dev/null
@@ -0,0 +1,27 @@
+using Content.Client.UserInterface.Controls;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Disposal.Mailing
+{
+    /// <summary>
+    /// Client-side UI used to control a <see cref="Shared.Disposal.Components.MailingUnitComponent"/>
+    /// </summary>
+    [GenerateTypedNameReferences]
+    public sealed partial class MailingUnitWindow : FancyWindow
+    {
+        public TimeSpan FullPressure;
+
+        public MailingUnitWindow()
+        {
+            RobustXamlLoader.Load(this);
+        }
+
+        protected override void FrameUpdate(FrameEventArgs args)
+        {
+            base.FrameUpdate(args);
+            PressureBar.UpdatePressure(FullPressure);
+        }
+    }
+}
similarity index 96%
rename from Content.Client/Disposal/UI/PressureBar.cs
rename to Content.Client/Disposal/PressureBar.cs
index bff95ca4306ac90cadb712d39fa7db84762f135e..7b2ebacaf7c7a9703abed6dbff5d9ea8358cd547 100644 (file)
@@ -1,9 +1,10 @@
 using Content.Shared.Disposal;
+using Content.Shared.Disposal.Unit;
 using Robust.Client.Graphics;
 using Robust.Client.UserInterface.Controls;
 using Robust.Shared.Timing;
 
-namespace Content.Client.Disposal.UI;
+namespace Content.Client.Disposal;
 
 public sealed class PressureBar : ProgressBar
 {
diff --git a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs
deleted file mode 100644 (file)
index da548c1..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using Content.Shared.Disposal;
-using Content.Shared.Disposal.Components;
-using Content.Shared.DragDrop;
-using Content.Shared.Emag.Systems;
-using Robust.Client.GameObjects;
-using Robust.Client.Animations;
-using Robust.Client.Graphics;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.GameStates;
-using Robust.Shared.Physics.Events;
-using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent;
-
-namespace Content.Client.Disposal.Systems;
-
-public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
-{
-    [Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
-    [Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
-    [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
-
-    private const string AnimationKey = "disposal_unit_animation";
-
-    private const string DefaultFlushState = "disposal-flush";
-    private const string DefaultChargeState = "disposal-charging";
-
-    public override void Initialize()
-    {
-        base.Initialize();
-
-        SubscribeLocalEvent<DisposalUnitComponent, ComponentHandleState>(OnHandleState);
-        SubscribeLocalEvent<DisposalUnitComponent, PreventCollideEvent>(OnPreventCollide);
-        SubscribeLocalEvent<DisposalUnitComponent, CanDropTargetEvent>(OnCanDragDropOn);
-        SubscribeLocalEvent<DisposalUnitComponent, GotEmaggedEvent>(OnEmagged);
-
-        SubscribeLocalEvent<DisposalUnitComponent, ComponentInit>(OnComponentInit);
-        SubscribeLocalEvent<DisposalUnitComponent, AppearanceChangeEvent>(OnAppearanceChange);
-    }
-
-    private void OnHandleState(EntityUid uid, DisposalUnitComponent component, ref ComponentHandleState args)
-    {
-        if (args.Current is not DisposalUnitComponentState state)
-            return;
-
-        component.FlushSound = state.FlushSound;
-        component.State = state.State;
-        component.NextPressurized = state.NextPressurized;
-        component.AutomaticEngageTime = state.AutomaticEngageTime;
-        component.NextFlush = state.NextFlush;
-        component.Powered = state.Powered;
-        component.Engaged = state.Engaged;
-        component.RecentlyEjected.Clear();
-        component.RecentlyEjected.AddRange(EnsureEntityList<DisposalUnitComponent>(state.RecentlyEjected, uid));
-    }
-
-    public override bool HasDisposals(EntityUid? uid)
-    {
-        return HasComp<DisposalUnitComponent>(uid);
-    }
-
-    public override bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component)
-    {
-        if (component != null)
-            return true;
-
-        TryComp<DisposalUnitComponent>(uid, out var storage);
-        component = storage;
-        return component != null;
-    }
-
-    public override void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null)
-    {
-        return;
-    }
-
-    private void OnComponentInit(EntityUid uid, SharedDisposalUnitComponent sharedDisposalUnit, ComponentInit args)
-    {
-        if (!TryComp<SpriteComponent>(uid, out var sprite) || !TryComp<AppearanceComponent>(uid, out var appearance))
-            return;
-
-        UpdateState(uid, sharedDisposalUnit, sprite, appearance);
-    }
-
-    private void OnAppearanceChange(EntityUid uid, SharedDisposalUnitComponent unit, ref AppearanceChangeEvent args)
-    {
-        if (args.Sprite == null)
-            return;
-
-        UpdateState(uid, unit, args.Sprite, args.Component);
-    }
-
-    /// <summary>
-    /// Update visuals and tick animation
-    /// </summary>
-    private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, SpriteComponent sprite, AppearanceComponent appearance)
-    {
-        if (!_appearanceSystem.TryGetData<VisualState>(uid, Visuals.VisualState, out var state, appearance))
-            return;
-
-        sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == VisualState.UnAnchored);
-        sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == VisualState.Anchored);
-        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is VisualState.OverlayFlushing or VisualState.OverlayCharging);
-
-        var chargingState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseCharging, out var chargingLayer)
-            ? sprite.LayerGetState(chargingLayer)
-            : new RSI.StateId(DefaultChargeState);
-
-        // This is a transient state so not too worried about replaying in range.
-        if (state == VisualState.OverlayFlushing)
-        {
-            if (!_animationSystem.HasRunningAnimation(uid, AnimationKey))
-            {
-                var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer)
-                    ? sprite.LayerGetState(flushLayer)
-                    : new RSI.StateId(DefaultFlushState);
-
-                // Setup the flush animation to play
-                var anim = new Animation
-                {
-                    Length = unit.FlushDelay,
-                    AnimationTracks =
-                    {
-                        new AnimationTrackSpriteFlick
-                        {
-                            LayerKey = DisposalUnitVisualLayers.OverlayFlush,
-                            KeyFrames =
-                            {
-                                // Play the flush animation
-                                new AnimationTrackSpriteFlick.KeyFrame(flushState, 0),
-                                // Return to base state (though, depending on how the unit is
-                                // configured we might get an appearance change event telling
-                                // us to go to charging state)
-                                new AnimationTrackSpriteFlick.KeyFrame(chargingState, (float) unit.FlushDelay.TotalSeconds)
-                            }
-                        },
-                    }
-                };
-
-                if (unit.FlushSound != null)
-                {
-                    anim.AnimationTracks.Add(
-                        new AnimationTrackPlaySound
-                        {
-                            KeyFrames =
-                            {
-                                new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(unit.FlushSound), 0)
-                            }
-                        });
-                }
-
-                _animationSystem.Play(uid, anim, AnimationKey);
-            }
-        }
-        else if (state == VisualState.OverlayCharging)
-            sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState);
-        else
-            _animationSystem.Stop(uid, AnimationKey);
-
-        if (!_appearanceSystem.TryGetData<HandleState>(uid, Visuals.Handle, out var handleState, appearance))
-            handleState = HandleState.Normal;
-
-        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != HandleState.Normal);
-
-        if (!_appearanceSystem.TryGetData<LightStates>(uid, Visuals.Light, out var lightState, appearance))
-            lightState = LightStates.Off;
-
-        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging,
-                (lightState & LightStates.Charging) != 0);
-        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayReady,
-                (lightState & LightStates.Ready) != 0);
-        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFull,
-                (lightState & LightStates.Full) != 0);
-    }
-}
-
-public enum DisposalUnitVisualLayers : byte
-{
-    Unanchored,
-    Base,
-    BaseCharging,
-    OverlayFlush,
-    OverlayCharging,
-    OverlayReady,
-    OverlayFull,
-    OverlayEngaged
-}
similarity index 95%
rename from Content.Client/Disposal/UI/DisposalRouterBoundUserInterface.cs
rename to Content.Client/Disposal/Tube/DisposalRouterBoundUserInterface.cs
index 296e71d3a9502c8ef66c27b9affa35392cebe7cb..1116dc7257540baca2b57b1da5dfa680fce2f3c3 100644 (file)
@@ -1,9 +1,8 @@
 using JetBrains.Annotations;
-using Robust.Client.GameObjects;
 using Robust.Client.UserInterface;
 using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent;
 
-namespace Content.Client.Disposal.UI
+namespace Content.Client.Disposal.Tube
 {
     /// <summary>
     /// Initializes a <see cref="DisposalRouterWindow"/> and updates it when new server messages are received.
similarity index 90%
rename from Content.Client/Disposal/UI/DisposalRouterWindow.xaml.cs
rename to Content.Client/Disposal/Tube/DisposalRouterWindow.xaml.cs
index 39ee9fbe233069469ec6528eeec7fab5d53ffa9d..c9d7d286ebcd2887b9e377e79c8b32d515baf040 100644 (file)
@@ -1,11 +1,10 @@
 using Content.Shared.Disposal.Components;
 using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.CustomControls;
 using Robust.Client.UserInterface.XAML;
 using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent;
 
-namespace Content.Client.Disposal.UI
+namespace Content.Client.Disposal.Tube
 {
     /// <summary>
     /// Client-side UI used to control a <see cref="SharedDisposalRouterComponent"/>
similarity index 95%
rename from Content.Client/Disposal/UI/DisposalTaggerBoundUserInterface.cs
rename to Content.Client/Disposal/Tube/DisposalTaggerBoundUserInterface.cs
index 7fc0eb854017542c75be4ea7a4838b709eb1402f..5618e83ecfa1487ed45cad22a60faf1ff4c6d275 100644 (file)
@@ -1,9 +1,8 @@
 using JetBrains.Annotations;
-using Robust.Client.GameObjects;
 using Robust.Client.UserInterface;
 using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent;
 
-namespace Content.Client.Disposal.UI
+namespace Content.Client.Disposal.Tube
 {
     /// <summary>
     /// Initializes a <see cref="DisposalTaggerWindow"/> and updates it when new server messages are received.
similarity index 90%
rename from Content.Client/Disposal/UI/DisposalTaggerWindow.xaml.cs
rename to Content.Client/Disposal/Tube/DisposalTaggerWindow.xaml.cs
index b49d5d997b577a0c5c754a1731a8319626433bef..ae8286cf14a5643c153e86f1768c6ee3639ff619 100644 (file)
@@ -1,11 +1,10 @@
 using Content.Shared.Disposal.Components;
 using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.CustomControls;
 using Robust.Client.UserInterface.XAML;
 using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent;
 
-namespace Content.Client.Disposal.UI
+namespace Content.Client.Disposal.Tube
 {
     /// <summary>
     /// Client-side UI used to control a <see cref="SharedDisposalTaggerComponent"/>
diff --git a/Content.Client/Disposal/Tube/DisposalTubeSystem.cs b/Content.Client/Disposal/Tube/DisposalTubeSystem.cs
new file mode 100644 (file)
index 0000000..fd86f7e
--- /dev/null
@@ -0,0 +1,8 @@
+using Content.Shared.Disposal.Unit;
+
+namespace Content.Client.Disposal.Tube;
+
+public sealed class DisposalTubeSystem : SharedDisposalTubeSystem
+{
+
+}
diff --git a/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs b/Content.Client/Disposal/UI/DisposalUnitBoundUserInterface.cs
deleted file mode 100644 (file)
index d2bec6e..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-using Content.Client.Disposal.Systems;
-using Content.Shared.Disposal;
-using Content.Shared.Disposal.Components;
-using JetBrains.Annotations;
-using Robust.Client.GameObjects;
-using Robust.Client.UserInterface.Controls;
-using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent;
-
-namespace Content.Client.Disposal.UI
-{
-    /// <summary>
-    /// Initializes a <see cref="MailingUnitWindow"/> or a <see cref="DisposalUnitWindow"/> and updates it when new server messages are received.
-    /// </summary>
-    [UsedImplicitly]
-    public sealed class DisposalUnitBoundUserInterface : BoundUserInterface
-    {
-        // What are you doing here
-        [ViewVariables]
-        public MailingUnitWindow? MailingUnitWindow;
-
-        [ViewVariables]
-        public DisposalUnitWindow? DisposalUnitWindow;
-
-        public DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
-        {
-        }
-
-        private void ButtonPressed(UiButton button)
-        {
-            SendMessage(new UiButtonPressedMessage(button));
-            // If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs
-            // the pressure lerp up.
-        }
-
-        private void TargetSelected(ItemList.ItemListSelectedEventArgs args)
-        {
-            var item = args.ItemList[args.ItemIndex];
-            SendMessage(new TargetSelectedMessage(item.Text));
-        }
-
-        protected override void Open()
-        {
-            base.Open();
-
-            if (UiKey is MailingUnitUiKey)
-            {
-                MailingUnitWindow = new MailingUnitWindow();
-
-                MailingUnitWindow.OpenCenteredRight();
-                MailingUnitWindow.OnClose += Close;
-
-                MailingUnitWindow.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject);
-                MailingUnitWindow.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage);
-                MailingUnitWindow.Power.OnPressed += _ => ButtonPressed(UiButton.Power);
-
-                MailingUnitWindow.TargetListContainer.OnItemSelected += TargetSelected;
-            }
-            else if (UiKey is DisposalUnitUiKey)
-            {
-                DisposalUnitWindow = new DisposalUnitWindow();
-
-                DisposalUnitWindow.OpenCenteredRight();
-                DisposalUnitWindow.OnClose += Close;
-
-                DisposalUnitWindow.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject);
-                DisposalUnitWindow.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage);
-                DisposalUnitWindow.Power.OnPressed += _ => ButtonPressed(UiButton.Power);
-            }
-        }
-
-        protected override void UpdateState(BoundUserInterfaceState state)
-        {
-            base.UpdateState(state);
-
-            if (state is not MailingUnitBoundUserInterfaceState && state is not DisposalUnitBoundUserInterfaceState)
-            {
-                return;
-            }
-
-            switch (state)
-            {
-                case MailingUnitBoundUserInterfaceState mailingUnitState:
-                    MailingUnitWindow?.UpdateState(mailingUnitState);
-                    break;
-
-                case DisposalUnitBoundUserInterfaceState disposalUnitState:
-                    DisposalUnitWindow?.UpdateState(disposalUnitState);
-                    break;
-            }
-        }
-
-        protected override void Dispose(bool disposing)
-        {
-            base.Dispose(disposing);
-
-            if (!disposing)
-                return;
-
-            MailingUnitWindow?.Dispose();
-            DisposalUnitWindow?.Dispose();
-        }
-    }
-}
diff --git a/Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs b/Content.Client/Disposal/UI/DisposalUnitWindow.xaml.cs
deleted file mode 100644 (file)
index 3440fe2..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-using Content.Shared.Disposal.Components;
-using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.CustomControls;
-using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Timing;
-using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent;
-
-namespace Content.Client.Disposal.UI
-{
-    /// <summary>
-    /// Client-side UI used to control a <see cref="SharedDisposalUnitComponent"/>
-    /// </summary>
-    [GenerateTypedNameReferences]
-    public sealed partial class DisposalUnitWindow : DefaultWindow
-    {
-        public TimeSpan FullPressure;
-
-        public DisposalUnitWindow()
-        {
-            IoCManager.InjectDependencies(this);
-            RobustXamlLoader.Load(this);
-        }
-
-        /// <summary>
-        /// Update the interface state for the disposals window.
-        /// </summary>
-        /// <returns>true if we should stop updating every frame.</returns>
-        public void UpdateState(DisposalUnitBoundUserInterfaceState state)
-        {
-            Title = state.UnitName;
-            UnitState.Text = state.UnitState;
-            Power.Pressed = state.Powered;
-            Engage.Pressed = state.Engaged;
-            FullPressure = state.FullPressureTime;
-        }
-
-        protected override void FrameUpdate(FrameEventArgs args)
-        {
-            base.FrameUpdate(args);
-            PressureBar.UpdatePressure(FullPressure);
-        }
-    }
-}
diff --git a/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs b/Content.Client/Disposal/UI/MailingUnitWindow.xaml.cs
deleted file mode 100644 (file)
index 489d749..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-using Content.Shared.Disposal;
-using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.CustomControls;
-using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Timing;
-
-namespace Content.Client.Disposal.UI
-{
-    /// <summary>
-    /// Client-side UI used to control a <see cref="MailingUnitComponent"/>
-    /// </summary>
-    [GenerateTypedNameReferences]
-    public sealed partial class MailingUnitWindow : DefaultWindow
-    {
-        public TimeSpan FullPressure;
-
-        public MailingUnitWindow()
-        {
-            RobustXamlLoader.Load(this);
-        }
-
-        /// <summary>
-        /// Update the interface state for the disposals window.
-        /// </summary>
-        /// <returns>true if we should stop updating every frame.</returns>
-        public bool UpdateState(MailingUnitBoundUserInterfaceState state)
-        {
-            var disposalState = state.DisposalState;
-
-            Title = Loc.GetString("ui-mailing-unit-window-title", ("tag", state.Tag ?? " "));
-            UnitState.Text = disposalState.UnitState;
-            FullPressure = disposalState.FullPressureTime;
-            var pressureReached = PressureBar.UpdatePressure(disposalState.FullPressureTime);
-            Power.Pressed = disposalState.Powered;
-            Engage.Pressed = disposalState.Engaged;
-
-            //UnitTag.Text = state.Tag;
-            Target.Text = state.Target;
-
-            TargetListContainer.Clear();
-            foreach (var target in state.TargetList)
-            {
-                TargetListContainer.AddItem(target);
-            }
-
-            return !disposalState.Powered || pressureReached;
-        }
-
-        protected override void FrameUpdate(FrameEventArgs args)
-        {
-            base.FrameUpdate(args);
-            PressureBar.UpdatePressure(FullPressure);
-        }
-    }
-}
diff --git a/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs b/Content.Client/Disposal/Unit/DisposalUnitBoundUserInterface.cs
new file mode 100644 (file)
index 0000000..6238629
--- /dev/null
@@ -0,0 +1,63 @@
+using Content.Client.Disposal.Mailing;
+using Content.Client.Power.EntitySystems;
+using Content.Shared.Disposal.Components;
+using JetBrains.Annotations;
+using Robust.Client.UserInterface;
+
+namespace Content.Client.Disposal.Unit
+{
+    /// <summary>
+    /// Initializes a <see cref="MailingUnitWindow"/> or a <see cref="_disposalUnitWindow"/> and updates it when new server messages are received.
+    /// </summary>
+    [UsedImplicitly]
+    public sealed class DisposalUnitBoundUserInterface : BoundUserInterface
+    {
+        [ViewVariables] private DisposalUnitWindow? _disposalUnitWindow;
+
+        public DisposalUnitBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+        {
+        }
+
+        private void ButtonPressed(DisposalUnitComponent.UiButton button)
+        {
+            SendPredictedMessage(new DisposalUnitComponent.UiButtonPressedMessage(button));
+            // If we get client-side power stuff then we can predict the button presses but for now we won't as it stuffs
+            // the pressure lerp up.
+        }
+
+        protected override void Open()
+        {
+            base.Open();
+
+            _disposalUnitWindow = this.CreateWindow<DisposalUnitWindow>();
+
+            _disposalUnitWindow.OpenCenteredRight();
+
+            _disposalUnitWindow.Eject.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Eject);
+            _disposalUnitWindow.Engage.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Engage);
+            _disposalUnitWindow.Power.OnPressed += _ => ButtonPressed(DisposalUnitComponent.UiButton.Power);
+
+            if (EntMan.TryGetComponent(Owner, out DisposalUnitComponent? component))
+            {
+                Refresh((Owner, component));
+            }
+        }
+
+        public void Refresh(Entity<DisposalUnitComponent> entity)
+        {
+            if (_disposalUnitWindow == null)
+                return;
+
+            var disposalSystem = EntMan.System<DisposalUnitSystem>();
+
+            _disposalUnitWindow.Title = EntMan.GetComponent<MetaDataComponent>(entity.Owner).EntityName;
+
+            var state = disposalSystem.GetState(entity.Owner, entity.Comp);
+
+            _disposalUnitWindow.UnitState.Text = Loc.GetString($"disposal-unit-state-{state}");
+            _disposalUnitWindow.Power.Pressed = EntMan.System<PowerReceiverSystem>().IsPowered(Owner);
+            _disposalUnitWindow.Engage.Pressed = entity.Comp.Engaged;
+            _disposalUnitWindow.FullPressure = disposalSystem.EstimatedFullPressure(entity.Owner, entity.Comp);
+        }
+    }
+}
diff --git a/Content.Client/Disposal/Unit/DisposalUnitSystem.cs b/Content.Client/Disposal/Unit/DisposalUnitSystem.cs
new file mode 100644 (file)
index 0000000..30ca320
--- /dev/null
@@ -0,0 +1,156 @@
+using Content.Shared.Disposal.Components;
+using Content.Shared.Disposal.Unit;
+using Robust.Client.Animations;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Shared.Audio.Systems;
+
+namespace Content.Client.Disposal.Unit;
+
+public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
+{
+    [Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
+    [Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
+    [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+    [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
+
+    private const string AnimationKey = "disposal_unit_animation";
+
+    private const string DefaultFlushState = "disposal-flush";
+    private const string DefaultChargeState = "disposal-charging";
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<DisposalUnitComponent, AfterAutoHandleStateEvent>(OnHandleState);
+
+        SubscribeLocalEvent<DisposalUnitComponent, AppearanceChangeEvent>(OnAppearanceChange);
+    }
+
+    private void OnHandleState(EntityUid uid, DisposalUnitComponent component, ref AfterAutoHandleStateEvent args)
+    {
+        UpdateUI((uid, component));
+    }
+
+    protected override void UpdateUI(Entity<DisposalUnitComponent> entity)
+    {
+        if (_uiSystem.TryGetOpenUi<DisposalUnitBoundUserInterface>(entity.Owner, DisposalUnitComponent.DisposalUnitUiKey.Key, out var bui))
+        {
+            bui.Refresh(entity);
+        }
+    }
+
+    protected override void OnDisposalInit(Entity<DisposalUnitComponent> ent, ref ComponentInit args)
+    {
+        base.OnDisposalInit(ent, ref args);
+
+        if (!TryComp<SpriteComponent>(ent, out var sprite) || !TryComp<AppearanceComponent>(ent, out var appearance))
+            return;
+
+        UpdateState(ent, sprite, appearance);
+    }
+
+    private void OnAppearanceChange(Entity<DisposalUnitComponent> ent, ref AppearanceChangeEvent args)
+    {
+        if (args.Sprite == null)
+            return;
+
+        UpdateState(ent, args.Sprite, args.Component);
+    }
+
+    /// <summary>
+    /// Update visuals and tick animation
+    /// </summary>
+    private void UpdateState(Entity<DisposalUnitComponent> ent, SpriteComponent sprite, AppearanceComponent appearance)
+    {
+        if (!_appearanceSystem.TryGetData<DisposalUnitComponent.VisualState>(ent, DisposalUnitComponent.Visuals.VisualState, out var state, appearance))
+            return;
+
+        sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == DisposalUnitComponent.VisualState.UnAnchored);
+        sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == DisposalUnitComponent.VisualState.Anchored);
+        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is DisposalUnitComponent.VisualState.OverlayFlushing or DisposalUnitComponent.VisualState.OverlayCharging);
+
+        var chargingState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseCharging, out var chargingLayer)
+            ? sprite.LayerGetState(chargingLayer)
+            : new RSI.StateId(DefaultChargeState);
+
+        // This is a transient state so not too worried about replaying in range.
+        if (state == DisposalUnitComponent.VisualState.OverlayFlushing)
+        {
+            if (!_animationSystem.HasRunningAnimation(ent, AnimationKey))
+            {
+                var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer)
+                    ? sprite.LayerGetState(flushLayer)
+                    : new RSI.StateId(DefaultFlushState);
+
+                // Setup the flush animation to play
+                var anim = new Animation
+                {
+                    Length = ent.Comp.FlushDelay,
+                    AnimationTracks =
+                    {
+                        new AnimationTrackSpriteFlick
+                        {
+                            LayerKey = DisposalUnitVisualLayers.OverlayFlush,
+                            KeyFrames =
+                            {
+                                // Play the flush animation
+                                new AnimationTrackSpriteFlick.KeyFrame(flushState, 0),
+                                // Return to base state (though, depending on how the unit is
+                                // configured we might get an appearance change event telling
+                                // us to go to charging state)
+                                new AnimationTrackSpriteFlick.KeyFrame(chargingState, (float) ent.Comp.FlushDelay.TotalSeconds)
+                            }
+                        },
+                    }
+                };
+
+                if (ent.Comp.FlushSound != null)
+                {
+                    anim.AnimationTracks.Add(
+                        new AnimationTrackPlaySound
+                        {
+                            KeyFrames =
+                            {
+                                new AnimationTrackPlaySound.KeyFrame(_audioSystem.ResolveSound(ent.Comp.FlushSound), 0)
+                            }
+                        });
+                }
+
+                _animationSystem.Play(ent, anim, AnimationKey);
+            }
+        }
+        else if (state == DisposalUnitComponent.VisualState.OverlayCharging)
+            sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, chargingState);
+        else
+            _animationSystem.Stop(ent.Owner, AnimationKey);
+
+        if (!_appearanceSystem.TryGetData<DisposalUnitComponent.HandleState>(ent, DisposalUnitComponent.Visuals.Handle, out var handleState, appearance))
+            handleState = DisposalUnitComponent.HandleState.Normal;
+
+        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != DisposalUnitComponent.HandleState.Normal);
+
+        if (!_appearanceSystem.TryGetData<DisposalUnitComponent.LightStates>(ent, DisposalUnitComponent.Visuals.Light, out var lightState, appearance))
+            lightState = DisposalUnitComponent.LightStates.Off;
+
+        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging,
+                (lightState & DisposalUnitComponent.LightStates.Charging) != 0);
+        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayReady,
+                (lightState & DisposalUnitComponent.LightStates.Ready) != 0);
+        sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFull,
+                (lightState & DisposalUnitComponent.LightStates.Full) != 0);
+    }
+}
+
+public enum DisposalUnitVisualLayers : byte
+{
+    Unanchored,
+    Base,
+    BaseCharging,
+    OverlayFlush,
+    OverlayCharging,
+    OverlayReady,
+    OverlayFull,
+    OverlayEngaged
+}
similarity index 72%
rename from Content.Client/Disposal/UI/DisposalUnitWindow.xaml
rename to Content.Client/Disposal/Unit/DisposalUnitWindow.xaml
index c8acef98ea6f36f0dbf195f44cc1e7f024a1f929..60ca7ba0db44bef84c869c7470a1761350f27f20 100644 (file)
@@ -1,20 +1,21 @@
-<DefaultWindow xmlns="https://spacestation14.io"
-               xmlns:ui="clr-namespace:Content.Client.Disposal.UI"
-               MinSize="300 140"
-            SetSize="300 140"
+<controls:FancyWindow xmlns="https://spacestation14.io"
+                      xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
+                      xmlns:disposal="clr-namespace:Content.Client.Disposal"
+                      MinSize="300 140"
+            SetSize="300 160"
             Resizable="False">
-    <BoxContainer Orientation="Vertical">
+    <BoxContainer Orientation="Vertical" Margin="10">
         <BoxContainer Orientation="Horizontal"
                       SeparationOverride="4">
             <Label Text="{Loc 'ui-disposal-unit-label-state'}" />
-            <Label Name="UnitState"
+            <Label Name="UnitState" Access="Public"
                    Text="{Loc 'ui-disposal-unit-label-status'}" />
         </BoxContainer>
         <Control MinSize="0 5" />
         <BoxContainer Orientation="Horizontal"
                       SeparationOverride="4">
             <Label Text="{Loc 'ui-disposal-unit-label-pressure'}" />
-            <ui:PressureBar Name="PressureBar"
+            <disposal:PressureBar Name="PressureBar"
                          MinSize="190 20"
                          HorizontalAlignment="Right"
                          MinValue="0"
@@ -23,7 +24,7 @@
                          Value="0.5" />
         </BoxContainer>
         <Control MinSize="0 10" />
-        <BoxContainer Orientation="Horizontal">
+        <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
             <Button Name="Engage"
                     Access="Public"
                     Text="{Loc 'ui-disposal-unit-button-flush'}"
@@ -39,4 +40,4 @@
                          StyleClasses="OpenLeft" />
         </BoxContainer>
     </BoxContainer>
-</DefaultWindow>
+</controls:FancyWindow>
diff --git a/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs b/Content.Client/Disposal/Unit/DisposalUnitWindow.xaml.cs
new file mode 100644 (file)
index 0000000..db4d58e
--- /dev/null
@@ -0,0 +1,28 @@
+using Content.Client.UserInterface.Controls;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Timing;
+
+namespace Content.Client.Disposal.Unit
+{
+    /// <summary>
+    /// Client-side UI used to control a <see cref="Shared.Disposal.Components.DisposalUnitComponent"/>
+    /// </summary>
+    [GenerateTypedNameReferences]
+    public sealed partial class DisposalUnitWindow : FancyWindow
+    {
+        public TimeSpan FullPressure;
+
+        public DisposalUnitWindow()
+        {
+            IoCManager.InjectDependencies(this);
+            RobustXamlLoader.Load(this);
+        }
+
+        protected override void FrameUpdate(FrameEventArgs args)
+        {
+            base.FrameUpdate(args);
+            PressureBar.UpdatePressure(FullPressure);
+        }
+    }
+}
index 0b9fd6db0565f3ad309f64556ff0c2066adb0be1..131fa54d433bc3a7b68313cdb4b30a7928989c06 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Shared.DeviceNetwork;
 using Content.Shared.DeviceNetwork.Systems;
 
 namespace Content.Client.NetworkConfigurator.Systems;
index 319ab5c56dc2b254c1ad2e26de8c9657f303a6e8..0b19d8acedb3f5c979dbc01f03a6bb3c0d4eaf12 100644 (file)
@@ -3,6 +3,7 @@ using Content.Client.Power.Components;
 using Content.Shared.Power.Components;
 using Content.Shared.Power.EntitySystems;
 using Content.Shared.Examine;
+using Content.Shared.Power;
 using Robust.Shared.GameStates;
 
 namespace Content.Client.Power.EntitySystems;
@@ -26,9 +27,20 @@ public sealed class PowerReceiverSystem : SharedPowerReceiverSystem
         if (args.Current is not ApcPowerReceiverComponentState state)
             return;
 
+        var powerChanged = component.Powered != state.Powered;
         component.Powered = state.Powered;
         component.NeedsPower = state.NeedsPower;
         component.PowerDisabled = state.PowerDisabled;
+        // SO client systems can handle it. The main reason for this is we can't guarantee compstate ordering.
+
+        if (powerChanged)
+            RaisePower((uid, component));
+    }
+
+    protected override void RaisePower(Entity<SharedApcPowerReceiverComponent> entity)
+    {
+        var ev = new PowerChangedEvent(entity.Comp.Powered, 0f);
+        RaiseLocalEvent(entity.Owner, ref ev);
     }
 
     public override bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component)
index cb949ebbfdbf1ef7df937bb9661db2fd673ee130..9b0c487a02161bad6a2fd2f6e0afd3de070a018e 100644 (file)
@@ -11,9 +11,6 @@ public sealed class StorageFillVisualizerSystem : VisualizerSystem<StorageFillVi
         if (args.Sprite == null)
             return;
 
-        if (!TryComp(uid, out SpriteComponent? sprite))
-            return;
-
         if (!AppearanceSystem.TryGetData<int>(uid, StorageFillVisuals.FillLevel, out var level, args.Component))
             return;
 
index b37f7cfa46875515000b01becc23fb2557de5387..fdc0e1a4d4aefd9a736c349ab2d34ad1fe0c8c98 100644 (file)
@@ -1,12 +1,10 @@
 using System.Numerics;
-using Content.Server.DeviceNetwork;
 using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Shared.DeviceNetwork;
 using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
 using Robust.Shared.Map;
-using Robust.Shared.Map.Components;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.IntegrationTests.Tests.DeviceNetwork
 {
index 400d85f2ccb481522a57a1e460a6d420d307f1e4..c314f412346ea5100b1e5383887f5bc8d81a9c06 100644 (file)
@@ -1,31 +1,30 @@
-using Content.Server.DeviceNetwork.Components;
-using Content.Server.DeviceNetwork.Systems;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Robust.Shared.GameObjects;
 using Robust.Shared.Reflection;
+using Content.Shared.DeviceNetwork.Components;
 
-namespace Content.IntegrationTests.Tests.DeviceNetwork
+namespace Content.IntegrationTests.Tests.DeviceNetwork;
+
+[Reflect(false)]
+public sealed class DeviceNetworkTestSystem : EntitySystem
 {
-    [Reflect(false)]
-    public sealed class DeviceNetworkTestSystem : EntitySystem
-    {
-        public NetworkPayload LastPayload = default;
+    public NetworkPayload LastPayload = default;
 
-        public override void Initialize()
-        {
-            base.Initialize();
+    public override void Initialize()
+    {
+        base.Initialize();
 
-            SubscribeLocalEvent<DeviceNetworkComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
-        }
+        SubscribeLocalEvent<DeviceNetworkComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
+    }
 
-        public void SendBaselineTestEvent(EntityUid uid)
-        {
-            RaiseLocalEvent(uid, new DeviceNetworkPacketEvent(0, "", 0, "", uid, new NetworkPayload()));
-        }
+    public void SendBaselineTestEvent(EntityUid uid)
+    {
+        RaiseLocalEvent(uid, new DeviceNetworkPacketEvent(0, "", 0, "", uid, new NetworkPayload()));
+    }
 
-        private void OnPacketReceived(EntityUid uid, DeviceNetworkComponent component, DeviceNetworkPacketEvent args)
-        {
-            LastPayload = args.Data;
-        }
+    private void OnPacketReceived(EntityUid uid, DeviceNetworkComponent component, DeviceNetworkPacketEvent args)
+    {
+        LastPayload = args.Data;
     }
 }
index 9109fdbe4f7638a4ce6f98bc61713b108814bb4d..8cfe8fbfcda45d47f5ba79428e4081385d7084d8 100644 (file)
@@ -1,13 +1,12 @@
 #nullable enable annotations
 using System.Linq;
 using System.Numerics;
-using Content.Server.Disposal.Tube.Components;
-using Content.Server.Disposal.Unit.Components;
-using Content.Server.Disposal.Unit.EntitySystems;
+using Content.Server.Disposal.Unit;
 using Content.Server.Power.Components;
-using Content.Shared.Disposal;
+using Content.Server.Power.EntitySystems;
 using Content.Shared.Disposal.Components;
-using NUnit.Framework;
+using Content.Shared.Disposal.Tube;
+using Content.Shared.Disposal.Unit;
 using Robust.Shared.GameObjects;
 using Robust.Shared.Reflection;
 
@@ -30,7 +29,6 @@ namespace Content.IntegrationTests.Tests.Disposal
                 {
                     var (_, toInsert, unit) = ev;
                     var insertTransform = EntityManager.GetComponent<TransformComponent>(toInsert);
-                    var unitTransform = EntityManager.GetComponent<TransformComponent>(unit);
                     // Not in a tube yet
                     Assert.That(insertTransform.ParentUid, Is.EqualTo(unit));
                 }, after: new[] { typeof(SharedDisposalUnitSystem) });
@@ -163,6 +161,8 @@ namespace Content.IntegrationTests.Tests.Disposal
             var entityManager = server.ResolveDependency<IEntityManager>();
             var xformSystem = entityManager.System<SharedTransformSystem>();
             var disposalSystem = entityManager.System<DisposalUnitSystem>();
+            var power = entityManager.System<PowerReceiverSystem>();
+
             await server.WaitAssertion(() =>
             {
                 // Spawn the entities
@@ -191,7 +191,7 @@ namespace Content.IntegrationTests.Tests.Disposal
                 xformSystem.AnchorEntity(unitUid, entityManager.GetComponent<TransformComponent>(unitUid));
 
                 // No power
-                Assert.That(unitComponent.Powered, Is.False);
+                Assert.That(power.IsPowered(unitUid), Is.False);
 
                 // Can't insert the trunk or the unit into itself
                 UnitInsertContains(unitUid, unitComponent, false, disposalSystem, disposalUnit, disposalTrunk);
@@ -227,9 +227,9 @@ namespace Content.IntegrationTests.Tests.Disposal
             await server.WaitAssertion(() =>
             {
                 // Remove power need
-                Assert.That(entityManager.TryGetComponent(disposalUnit, out ApcPowerReceiverComponent power));
-                power!.NeedsPower = false;
-                unitComponent.Powered = true; //Power state changed event doesn't get fired smh
+                Assert.That(entityManager.TryGetComponent(disposalUnit, out ApcPowerReceiverComponent powerComp));
+                power.SetNeedsPower(disposalUnit, false);
+                powerComp.Powered = true;
 
                 // Flush with a mob and an item
                 Flush(disposalUnit, unitComponent, true, disposalSystem, human, wrench);
index c953277aad609f31e6bf4754508aa853773e113c..d94a277173f261f19929aafc9c4df68451a787e4 100644 (file)
@@ -2,7 +2,6 @@ using Content.Server.Administration.Logs;
 using Content.Server.Administration.Managers;
 using Content.Server.Administration.UI;
 using Content.Server.Disposal.Tube;
-using Content.Server.Disposal.Tube.Components;
 using Content.Server.EUI;
 using Content.Server.GameTicking;
 using Content.Server.Ghost.Roles;
@@ -24,7 +23,6 @@ using Content.Shared.Verbs;
 using Robust.Server.Console;
 using Robust.Server.GameObjects;
 using Robust.Shared.Console;
-using Robust.Shared.Map;
 using Robust.Shared.Map.Components;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
index 6e3a11b5f939e6bdcb63d18adac0b4e20cd3bade..f4613b1d05eb0a0ebd6eeedc47f65b5b84c38175 100644 (file)
@@ -1,5 +1,4 @@
 using Content.Server.Atmos.Monitor.Components;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Pinpointer;
 using Content.Server.Power.Components;
index 5ecadc71542e65275f489988352fab9d282ebf39..8c786e5b216e9b26b533578a739887fa2a980f1f 100644 (file)
@@ -17,6 +17,7 @@ using Robust.Shared.Map.Components;
 using Robust.Shared.Timing;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Atmos.Consoles;
 
@@ -360,7 +361,7 @@ public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleS
             chunk.AtmosPipeData[index] = atmosPipeData & ~mask;
         }
 
-        // Rebuild the tile's pipe data 
+        // Rebuild the tile's pipe data
         foreach (var ent in _sharedMapSystem.GetAnchoredEntities(gridUid, grid, coords))
         {
             if (!TryComp<AtmosPipeColorComponent>(ent, out var entAtmosPipeColor))
index 0ed91a9bc866392ab496ae62d03befa0c5dd769f..0ed33eaa46134284880a5fb3e67a9ce28e2d4b34 100644 (file)
@@ -1,11 +1,8 @@
 using Content.Server.Atmos.Monitor.Components;
 using Content.Server.Atmos.Piping.Components;
 using Content.Server.DeviceLinking.Systems;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Popups;
-using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
 using Content.Shared.Access.Components;
 using Content.Shared.Access.Systems;
@@ -22,8 +19,9 @@ using Content.Shared.Interaction;
 using Content.Shared.Power;
 using Content.Shared.Wires;
 using Robust.Server.GameObjects;
-using Robust.Shared.Player;
 using System.Linq;
+using Content.Shared.DeviceNetwork.Events;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Atmos.Monitor.Systems;
 
index 81a3968e6ff04aff352bed5c65494f06c00bdc43..918b0f33ff162758cb92428fabdcb64f2e9d2cdc 100644 (file)
@@ -1,19 +1,17 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using Content.Server.Atmos.Monitor.Components;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
-using Content.Server.Power.Components;
 using Content.Shared.Atmos.Monitor;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Power;
 using Content.Shared.Tag;
 using Robust.Server.Audio;
 using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
 using Robust.Shared.Prototypes;
-using Robust.Shared.Utility;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Atmos.Monitor.Systems;
 
index 99cf0109bbb3645356662ec107c2de926a89a7b2..520afe0c58622cf04ddc78f630d7044e5e1107c8 100644 (file)
@@ -15,6 +15,7 @@ using Content.Shared.Atmos.Monitor;
 using Content.Shared.Atmos.Piping.Components;
 using Content.Shared.Database;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Power;
 using Content.Shared.Tag;
 using Robust.Shared.Prototypes;
index 6d768c129261b4516dab13eb7563eb427347690e..d7a9a188f90a1654648cba482a54aa1f3fb593c7 100644 (file)
@@ -1,18 +1,12 @@
-using Content.Server.AlertLevel;
 using Content.Server.Atmos.Monitor.Components;
-using Content.Server.DeviceNetwork.Components;
-using Content.Server.DeviceNetwork.Systems;
-using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
 using Content.Shared.Access.Systems;
-using Content.Shared.AlertLevel;
 using Content.Shared.Atmos.Monitor;
 using Content.Shared.CCVar;
-using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Components;
 using Content.Shared.DeviceNetwork.Systems;
 using Content.Shared.Interaction;
 using Content.Shared.Emag.Systems;
-using Robust.Server.GameObjects;
 using Robust.Shared.Configuration;
 
 namespace Content.Server.Atmos.Monitor.Systems;
index 1171b36fb98133525f05b722ae437ef69f54fa65..c7c615455be1003a97c6622efcea68dc4896f032 100644 (file)
@@ -1,9 +1,9 @@
 using Content.Server.Atmos.Monitor.Components;
 using Content.Server.Atmos.Monitor.Systems;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.Wires;
 using Content.Shared.Atmos.Monitor.Components;
 using Content.Shared.Wires;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Atmos.Monitor;
 
index 1650427be533419065634c775bcbdc14ce37137f..c6c660614a5e48e79d527799d1929033ccef33a5 100644 (file)
@@ -1,8 +1,6 @@
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Atmos.Monitor.Systems;
 using Content.Server.Atmos.Piping.Components;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.NodeContainer.EntitySystems;
 using Content.Server.NodeContainer.Nodes;
@@ -12,8 +10,10 @@ using Content.Shared.Atmos.Piping.Binary.Systems;
 using Content.Shared.Atmos.Piping.Components;
 using Content.Shared.Audio;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using JetBrains.Annotations;
 using Robust.Server.GameObjects;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Atmos.Piping.Binary.EntitySystems
 {
index 8cc3f214369503b523f59395186ced2b6db1fd0d..3020b94d4920bd0438977f09da2d378a5e566fd9 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Server.Atmos.Piping.Binary.Components;
 using Content.Server.Atmos.Piping.Unary.EntitySystems;
 using Content.Shared.Atmos;
 using Content.Shared.Atmos.Piping.Binary.Components;
index 827ba0bda5cd9a084f85c7569d969633e699a8e6..e4f5f99ed73bb82e4d378d738fbd4ac6f07edaa6 100644 (file)
@@ -2,10 +2,7 @@ using Content.Server.Atmos.EntitySystems;
 using Content.Server.Atmos.Monitor.Systems;
 using Content.Server.Atmos.Piping.Components;
 using Content.Server.Atmos.Piping.Unary.Components;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
-using Content.Server.NodeContainer;
 using Content.Server.NodeContainer.EntitySystems;
 using Content.Server.NodeContainer.Nodes;
 using Content.Server.Power.Components;
@@ -18,7 +15,9 @@ using Content.Shared.UserInterface;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Database;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Examine;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Atmos.Piping.Unary.EntitySystems
 {
index e9f27a1958e966ef692b1fa1474ee89a0d2f1199..89dfb6aa9d4fea3c54ab4ebd549c3d43848ff5ca 100644 (file)
@@ -3,8 +3,6 @@ using Content.Server.Atmos.Monitor.Systems;
 using Content.Server.Atmos.Piping.Components;
 using Content.Server.Atmos.Piping.Unary.Components;
 using Content.Server.DeviceLinking.Systems;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.NodeContainer.EntitySystems;
 using Content.Server.NodeContainer.Nodes;
@@ -20,7 +18,9 @@ using Content.Shared.Audio;
 using Content.Shared.Database;
 using Content.Shared.DeviceLinking.Events;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Components;
 using Content.Shared.DoAfter;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Power;
index 38d75701d737d2b12b52dc9869e64eb9f0e91368..b5c8a9c1a94b4a2ccd2c39c5ece0e3d8059bcae5 100644 (file)
@@ -1,25 +1,22 @@
 using Content.Server.Atmos.EntitySystems;
-using Content.Server.Atmos.Monitor.Components;
 using Content.Server.Atmos.Monitor.Systems;
 using Content.Server.Atmos.Piping.Components;
 using Content.Server.Atmos.Piping.Unary.Components;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
-using Content.Server.NodeContainer;
 using Content.Server.NodeContainer.EntitySystems;
 using Content.Server.NodeContainer.Nodes;
-using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Atmos;
-using Content.Shared.Atmos.Piping.Unary.Visuals;
 using Content.Shared.Atmos.Monitor;
 using Content.Shared.Atmos.Piping.Components;
 using Content.Shared.Atmos.Piping.Unary.Components;
+using Content.Shared.Atmos.Piping.Unary.Visuals;
 using Content.Shared.Audio;
 using Content.Shared.Database;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Power;
 using Content.Shared.Tools.Systems;
 using JetBrains.Annotations;
index 8637e44e62a21ccf708930dff099600b2a83f505..405d1cee9fe445f0da8085fb74ab6da1cd837f6a 100644 (file)
@@ -3,6 +3,7 @@ using System.Linq;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.PDA;
 using Content.Shared.CartridgeLoader;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Interaction;
 using Robust.Server.Containers;
 using Robust.Server.GameObjects;
index f01be1fc715fac17ba908f9aeb627db36d0ca4d8..aaeaf0325eac0342f429020a119b5e2d3a530e8d 100644 (file)
@@ -1,11 +1,10 @@
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Shared.CartridgeLoader;
 using Content.Shared.CartridgeLoader.Cartridges;
+using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Components;
 using Content.Shared.Popups;
 using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
-using Robust.Shared.Player;
 using Robust.Shared.Random;
 
 namespace Content.Server.CartridgeLoader.Cartridges;
index b4303ee50ad3d279a148e7e30eac5d07f931bef2..fc489c1823e141fc01a4b705df35bce22b6d56c7 100644 (file)
@@ -1,9 +1,7 @@
 using Content.Server.Administration.Logs;
 using Content.Server.AlertLevel;
 using Content.Server.Chat.Systems;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
-using Content.Server.Interaction;
 using Content.Server.Popups;
 using Content.Server.RoundEnd;
 using Content.Server.Screens.Components;
@@ -16,6 +14,7 @@ using Content.Shared.Chat;
 using Content.Shared.Communications;
 using Content.Shared.Database;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Components;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Popups;
 using Robust.Server.GameObjects;
index bf89c3f7ed13ab9474a69d7169b3d54f2907cdda..965571f883b7abe07f7b79f5b35391f0068e17b6 100644 (file)
@@ -1,91 +1,8 @@
 using Content.Shared.Configurable;
-using Content.Shared.Interaction;
-using Content.Shared.Tools.Components;
-using Content.Shared.Tools.Systems;
-using Robust.Server.GameObjects;
-using Robust.Shared.Containers;
-using Robust.Shared.Player;
-using static Content.Shared.Configurable.ConfigurationComponent;
 
 namespace Content.Server.Configurable;
 
-public sealed class ConfigurationSystem : EntitySystem
+public sealed class ConfigurationSystem : SharedConfigurationSystem
 {
-    [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
-    [Dependency] private readonly SharedToolSystem _toolSystem = default!;
 
-    public override void Initialize()
-    {
-        base.Initialize();
-
-        SubscribeLocalEvent<ConfigurationComponent, ConfigurationUpdatedMessage>(OnUpdate);
-        SubscribeLocalEvent<ConfigurationComponent, ComponentStartup>(OnStartup);
-        SubscribeLocalEvent<ConfigurationComponent, InteractUsingEvent>(OnInteractUsing);
-        SubscribeLocalEvent<ConfigurationComponent, ContainerIsInsertingAttemptEvent>(OnInsert);
-    }
-
-    private void OnInteractUsing(EntityUid uid, ConfigurationComponent component, InteractUsingEvent args)
-    {
-        // TODO use activatable ui system
-        if (args.Handled)
-            return;
-
-        if (!_toolSystem.HasQuality(args.Used, component.QualityNeeded))
-            return;
-
-        args.Handled = _uiSystem.TryOpenUi(uid, ConfigurationUiKey.Key, args.User);
-    }
-
-    private void OnStartup(EntityUid uid, ConfigurationComponent component, ComponentStartup args)
-    {
-        UpdateUi(uid, component);
-    }
-
-    private void UpdateUi(EntityUid uid, ConfigurationComponent component)
-    {
-        if (_uiSystem.HasUi(uid, ConfigurationUiKey.Key))
-            _uiSystem.SetUiState(uid, ConfigurationUiKey.Key, new ConfigurationBoundUserInterfaceState(component.Config));
-    }
-
-    private void OnUpdate(EntityUid uid, ConfigurationComponent component, ConfigurationUpdatedMessage args)
-    {
-        foreach (var key in component.Config.Keys)
-        {
-            var value = args.Config.GetValueOrDefault(key);
-
-            if (string.IsNullOrWhiteSpace(value) || component.Validation != null && !component.Validation.IsMatch(value))
-                continue;
-
-            component.Config[key] = value;
-        }
-
-        UpdateUi(uid, component);
-
-        var updatedEvent = new ConfigurationUpdatedEvent(component);
-        RaiseLocalEvent(uid, updatedEvent, false);
-
-        // TODO support float (spinbox) and enum (drop-down) configurations
-        // TODO support verbs.
-    }
-
-    private void OnInsert(EntityUid uid, ConfigurationComponent component, ContainerIsInsertingAttemptEvent args)
-    {
-        if (!_toolSystem.HasQuality(args.EntityUid, component.QualityNeeded))
-            return;
-
-        args.Cancel();
-    }
-
-    /// <summary>
-    /// Sent when configuration values got changes
-    /// </summary>
-    public sealed class ConfigurationUpdatedEvent : EntityEventArgs
-    {
-        public ConfigurationComponent Configuration;
-
-        public ConfigurationUpdatedEvent(ConfigurationComponent configuration)
-        {
-            Configuration = configuration;
-        }
-    }
 }
index 09176c423ca9f15e714afbef7a659ed5ae439e1d..1b87cb83efa54d8e726ca13d8cd367d523168086 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Administration.Logs;
+using Content.Shared.Containers;
 using Content.Shared.Database;
 using Content.Shared.Popups;
 using Content.Shared.Throwing;
@@ -52,10 +53,3 @@ public sealed class ThrowInsertContainerSystem : EntitySystem
             _adminLogger.Add(LogType.Landed, LogImpact.Low, $"{ToPrettyString(args.Thrown)} thrown by {ToPrettyString(args.Component.Thrower.Value):player} landed in {ToPrettyString(ent)}");
     }
 }
-
-/// <summary>
-/// Sent before the insertion is made.
-/// Allows preventing the insertion if any system on the entity should need to.
-/// </summary>
-[ByRefEvent]
-public record struct BeforeThrowInsertEvent(EntityUid ThrownEntity, bool Cancelled = false);
index b895ec190e4b56257f28904bde0f8a7a86085d1e..cec92db44cae886bee34ddc9bbe3a4b25ae2184b 100644 (file)
@@ -1,10 +1,10 @@
 using Content.Server.DeviceLinking.Components;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Shared.DeviceLinking;
 using Content.Shared.DeviceLinking.Events;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.DeviceLinking.Systems;
 
index 145e2a7dfcdcab70fd35b5cc9970f90068dbc1d8..107559826c0220bfa7afd9cc0645c00db3410812 100644 (file)
@@ -2,6 +2,7 @@ using Content.Server.DeviceLinking.Components;
 using Content.Server.DeviceNetwork;
 using Content.Server.Doors.Systems;
 using Content.Shared.DeviceLinking.Events;
+using Content.Shared.DeviceNetwork;
 using Content.Shared.Doors.Components;
 using Content.Shared.Doors;
 using JetBrains.Annotations;
index 2bf71e4e946e32f44a23fbf9109586287f91674c..a425122df41734a225368a6209c7c385ec505d54 100644 (file)
@@ -1,6 +1,6 @@
 using Content.Server.DeviceLinking.Components;
-using Content.Server.DeviceNetwork;
 using Content.Shared.DeviceLinking.Events;
+using Content.Shared.DeviceNetwork;
 
 namespace Content.Server.DeviceLinking.Systems;
 
index eaab3c986fea0a459a1f1a5a3fcb69d8b07be4fb..038851f4ff18ad5da4d43dea76804c51cbb6fee4 100644 (file)
@@ -2,6 +2,7 @@ using Content.Server.DeviceLinking.Components;
 using Content.Server.DeviceNetwork;
 using Content.Shared.DeviceLinking;
 using Content.Shared.DeviceLinking.Events;
+using Content.Shared.DeviceNetwork;
 using Content.Shared.Examine;
 using Content.Shared.Interaction;
 using Content.Shared.Popups;
index dbae7790f93437b5687fb1dac7623107f29f6532..45e4d2175070699ad6270f9cf03552ad884e678f 100644 (file)
@@ -2,6 +2,7 @@ using Content.Server.DeviceLinking.Components;
 using Content.Server.DeviceNetwork;
 using Content.Shared.DeviceLinking;
 using Content.Shared.DeviceLinking.Events;
+using Content.Shared.DeviceNetwork;
 
 namespace Content.Server.DeviceLinking.Systems;
 
index aa4623e41574f4e7fe77a1fdf97d0bc61870edbf..798a9b540ec5494d59114490b676cf39ee812e0f 100644 (file)
@@ -4,6 +4,7 @@ using Content.Server.NodeContainer.EntitySystems;
 using JetBrains.Annotations;
 using Content.Server.Power.EntitySystems;
 using Content.Server.Power.Nodes;
+using Content.Shared.DeviceNetwork.Events;
 
 namespace Content.Server.DeviceNetwork.Systems
 {
index 1988a07cea096c6a96a93d6e7ab867d95c9a532e..20f0dfacb171aab88a56859e17ed963100c04e6c 100644 (file)
@@ -1,9 +1,7 @@
 using System.Linq;
-using Content.Server.DeviceNetwork.Components;
-using Content.Shared.DeviceNetwork;
 using Content.Shared.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.DeviceNetwork.Systems;
-using Content.Shared.Interaction;
 using JetBrains.Annotations;
 using Robust.Shared.Map.Events;
 
index 0702a720424e0f1f7998a2da2631d4d8471c7baa..1905b752b837f33560fc3e9571dca233e31b4210 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.DeviceNetwork.Systems;
 using Robust.Server.GameObjects;
 
index 6e7bd255c5dc4c43e2810679b17b6237a99d23d2..615ed37f9e24705b3cd5a8cc598ead074abe6d70 100644 (file)
@@ -1,6 +1,7 @@
 using Content.Server.DeviceNetwork.Components;
 using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
+using Content.Shared.DeviceNetwork.Events;
 
 namespace Content.Server.DeviceNetwork.Systems;
 
index 20ee7a5dd1bdfc48ca124cac8e49b49a6ab3b6b7..4b28fd9bf9cc1dc5f9df55dfc38a620e2be0790b 100644 (file)
@@ -1,12 +1,12 @@
-using Content.Server.DeviceNetwork.Components;
 using Content.Shared.DeviceNetwork;
 using JetBrains.Annotations;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using System.Buffers;
 using System.Diagnostics.CodeAnalysis;
-using System.Numerics;
 using Content.Shared.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
+using Content.Shared.DeviceNetwork.Systems;
 using Content.Shared.Examine;
 
 namespace Content.Server.DeviceNetwork.Systems
@@ -16,7 +16,7 @@ namespace Content.Server.DeviceNetwork.Systems
     ///     Device networking allows machines and devices to communicate with each other while adhering to restrictions like range or being connected to the same powernet.
     /// </summary>
     [UsedImplicitly]
-    public sealed class DeviceNetworkSystem : EntitySystem
+    public sealed class DeviceNetworkSystem : SharedDeviceNetworkSystem
     {
         [Dependency] private readonly IRobustRandom _random = default!;
         [Dependency] private readonly IPrototypeManager _protoMan = default!;
@@ -60,16 +60,7 @@ namespace Content.Server.DeviceNetwork.Systems
             SwapQueues();
         }
 
-        /// <summary>
-        /// Sends the given payload as a device network packet to the entity with the given address and frequency.
-        /// Addresses are given to the DeviceNetworkComponent of an entity when connecting.
-        /// </summary>
-        /// <param name="uid">The EntityUid of the sending entity</param>
-        /// <param name="address">The address of the entity that the packet gets sent to. If null, the message is broadcast to all devices on that frequency (except the sender)</param>
-        /// <param name="frequency">The frequency to send on</param>
-        /// <param name="data">The data to be sent</param>
-        /// <returns>Returns true when the packet was successfully enqueued.</returns>
-        public bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, int? network = null, DeviceNetworkComponent? device = null)
+        public override bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, int? network = null, DeviceNetworkComponent? device = null)
         {
             if (!Resolve(uid, ref device, false))
                 return false;
@@ -368,96 +359,4 @@ namespace Content.Server.DeviceNetwork.Systems
             }
         }
     }
-
-    /// <summary>
-    /// Event raised before a device network packet is send.
-    /// Subscribed to by other systems to prevent the packet from being sent.
-    /// </summary>
-    public sealed class BeforePacketSentEvent : CancellableEntityEventArgs
-    {
-        /// <summary>
-        /// The EntityUid of the entity the packet was sent from.
-        /// </summary>
-        public readonly EntityUid Sender;
-
-        public readonly TransformComponent SenderTransform;
-
-        /// <summary>
-        ///     The senders current position in world coordinates.
-        /// </summary>
-        public readonly Vector2 SenderPosition;
-
-        /// <summary>
-        /// The network the packet will be sent to.
-        /// </summary>
-        public readonly string NetworkId;
-
-        public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition, string networkId)
-        {
-            Sender = sender;
-            SenderTransform = xform;
-            SenderPosition = senderPosition;
-            NetworkId = networkId;
-        }
-    }
-
-    /// <summary>
-    /// Sent to the sending entity before broadcasting network packets to recipients
-    /// </summary>
-    public sealed class BeforeBroadcastAttemptEvent : CancellableEntityEventArgs
-    {
-        public readonly IReadOnlySet<DeviceNetworkComponent> Recipients;
-        public HashSet<DeviceNetworkComponent>? ModifiedRecipients;
-
-        public BeforeBroadcastAttemptEvent(IReadOnlySet<DeviceNetworkComponent> recipients)
-        {
-            Recipients = recipients;
-        }
-    }
-
-    /// <summary>
-    /// Event raised when a device network packet gets sent.
-    /// </summary>
-    public sealed class DeviceNetworkPacketEvent : EntityEventArgs
-    {
-        /// <summary>
-        /// The id of the network that this packet is being sent on.
-        /// </summary>
-        public int NetId;
-
-        /// <summary>
-        /// The frequency the packet is sent on.
-        /// </summary>
-        public readonly uint Frequency;
-
-        /// <summary>
-        /// Address of the intended recipient. Null if the message was broadcast.
-        /// </summary>
-        public string? Address;
-
-        /// <summary>
-        /// The device network address of the sending entity.
-        /// </summary>
-        public readonly string SenderAddress;
-
-        /// <summary>
-        /// The entity that sent the packet.
-        /// </summary>
-        public EntityUid Sender;
-
-        /// <summary>
-        /// The data that is being sent.
-        /// </summary>
-        public readonly NetworkPayload Data;
-
-        public DeviceNetworkPacketEvent(int netId, string? address, uint frequency, string senderAddress, EntityUid sender, NetworkPayload data)
-        {
-            NetId = netId;
-            Address = address;
-            Frequency = frequency;
-            SenderAddress = senderAddress;
-            Sender = sender;
-            Data = data;
-        }
-    }
 }
index 9a4a81a4c0ad0666373c55f512823b4717a9adec..588020a9636b1d2722d5359e68523b321894a954 100644 (file)
@@ -1,7 +1,8 @@
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Components.Devices;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Interaction;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.DeviceNetwork.Systems.Devices
 {
index 645d28f6d240d7ed4cb04eebf4e5630bce31618b..6bcbe30456193b6a09bf956b24b419f899db3575 100644 (file)
@@ -1,7 +1,6 @@
 using System.Linq;
 using Content.Server.Administration.Logs;
 using Content.Server.DeviceLinking.Systems;
-using Content.Server.DeviceNetwork.Components;
 using Content.Shared.Access.Components;
 using Content.Shared.Access.Systems;
 using Content.Shared.Database;
index 6c997828fae5e139dae0e92304cfc1f8cb63a36b..8c1f48e93f6009ee41fc470ad467b03a2beb89f2 100644 (file)
@@ -1,9 +1,9 @@
 using System.Diagnostics.CodeAnalysis;
 using Content.Server.DeviceNetwork.Components;
 using Content.Server.Medical.CrewMonitoring;
-using Content.Server.Power.Components;
 using Content.Server.Station.Systems;
 using Content.Shared.Power;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.DeviceNetwork.Systems;
 
index 675cacc4d7765e268cc0e27e07c25213831b3edd..cebe1a3b9d0ec9f5bee345bb164b600f4bb57c5d 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Server.DeviceNetwork.Components;
 using Content.Server.Station.Systems;
+using Content.Shared.DeviceNetwork.Events;
 using JetBrains.Annotations;
 using Robust.Shared.Map;
 
index 758a333c7ae4479d94004971eeb145798cc78333..54bd5256c7e25a91102e03e31b1d2c75c3fb084d 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
 using JetBrains.Annotations;
 
 namespace Content.Server.DeviceNetwork.Systems
index 1741afd4ce3e5f93a0e9ebd08ad131ab7471f741..8bca47e041f37710da3c8cb5218f9f9ee339831e 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
 using JetBrains.Annotations;
 
 namespace Content.Server.DeviceNetwork.Systems
index 6249b9497d8504ee45751e1d826afb7af3d1895d..6ee282a3104dbe061d263bd929eafee7aa0c3cda 100644 (file)
@@ -1,204 +1,8 @@
-using Content.Server.Configurable;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
-using Content.Server.DeviceNetwork.Systems;
-using Content.Server.Disposal.Unit.EntitySystems;
-using Content.Server.Power.Components;
-using Content.Shared.DeviceNetwork;
-using Content.Shared.Disposal;
-using Content.Shared.Interaction;
-using Robust.Server.GameObjects;
-using Robust.Shared.Player;
-using Robust.Shared.Utility;
+using Content.Shared.Disposal.Mailing;
 
 namespace Content.Server.Disposal.Mailing;
 
-public sealed class MailingUnitSystem : EntitySystem
+public sealed class MailingUnitSystem : SharedMailingUnitSystem
 {
-    [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
-    [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
 
-    private const string MailTag = "mail";
-
-    private const string TagConfigurationKey = "tag";
-
-    private const string NetTag = "tag";
-    private const string NetSrc = "src";
-    private const string NetTarget = "target";
-    private const string NetCmdSent = "mail_sent";
-    private const string NetCmdRequest = "get_mailer_tag";
-    private const string NetCmdResponse = "mailer_tag";
-    public override void Initialize()
-    {
-        base.Initialize();
-
-        SubscribeLocalEvent<MailingUnitComponent, ComponentInit>(OnComponentInit);
-        SubscribeLocalEvent<MailingUnitComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
-        SubscribeLocalEvent<MailingUnitComponent, BeforeDisposalFlushEvent>(OnBeforeFlush);
-        SubscribeLocalEvent<MailingUnitComponent, ConfigurationSystem.ConfigurationUpdatedEvent>(OnConfigurationUpdated);
-        SubscribeLocalEvent<MailingUnitComponent, ActivateInWorldEvent>(HandleActivate, before: new[] { typeof(DisposalUnitSystem) });
-        SubscribeLocalEvent<MailingUnitComponent, DisposalUnitUIStateUpdatedEvent>(OnDisposalUnitUIStateChange);
-        SubscribeLocalEvent<MailingUnitComponent, TargetSelectedMessage>(OnTargetSelected);
-    }
-
-
-    private void OnComponentInit(EntityUid uid, MailingUnitComponent component, ComponentInit args)
-    {
-        UpdateTargetList(uid, component);
-    }
-
-    private void OnPacketReceived(EntityUid uid, MailingUnitComponent component, DeviceNetworkPacketEvent args)
-    {
-        if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || !IsPowered(uid))
-            return;
-
-        switch (command)
-        {
-            case NetCmdRequest:
-                SendTagRequestResponse(uid, args, component.Tag);
-                break;
-            case NetCmdResponse when args.Data.TryGetValue(NetTag, out string? tag):
-                //Add the received tag request response to the list of targets
-                component.TargetList.Add(tag);
-                UpdateUserInterface(uid, component);
-                break;
-        }
-    }
-
-    /// <summary>
-    /// Sends the given tag as a response to a <see cref="NetCmdRequest"/> if it's not null
-    /// </summary>
-    private void SendTagRequestResponse(EntityUid uid, DeviceNetworkPacketEvent args, string? tag)
-    {
-        if (tag == null)
-            return;
-
-        var payload = new NetworkPayload
-        {
-            [DeviceNetworkConstants.Command] = NetCmdResponse,
-            [NetTag] = tag
-        };
-
-        _deviceNetworkSystem.QueuePacket(uid, args.Address, payload, args.Frequency);
-    }
-
-    /// <summary>
-    /// Prevents the unit from flushing if no target is selected
-    /// </summary>
-    private void OnBeforeFlush(EntityUid uid, MailingUnitComponent component, BeforeDisposalFlushEvent args)
-    {
-        if (string.IsNullOrEmpty(component.Target))
-        {
-            args.Cancel();
-            return;
-        }
-
-        args.Tags.Add(MailTag);
-        args.Tags.Add(component.Target);
-
-        BroadcastSentMessage(uid, component);
-    }
-
-    /// <summary>
-    /// Broadcast that a mail was sent including the src and target tags
-    /// </summary>
-    private void BroadcastSentMessage(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null)
-    {
-        if (string.IsNullOrEmpty(component.Tag) || string.IsNullOrEmpty(component.Target) || !Resolve(uid, ref device))
-            return;
-
-        var payload = new NetworkPayload
-        {
-            [DeviceNetworkConstants.Command] = NetCmdSent,
-            [NetSrc] = component.Tag,
-            [NetTarget] = component.Target
-        };
-
-        _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device);
-    }
-
-    /// <summary>
-    /// Clears the units target list and broadcasts a <see cref="NetCmdRequest"/>.
-    /// The target list will then get populated with <see cref="NetCmdResponse"/> responses from all active mailing units on the same grid
-    /// </summary>
-    private void UpdateTargetList(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null)
-    {
-        if (!Resolve(uid, ref device, false))
-            return;
-
-        var payload = new NetworkPayload
-        {
-            [DeviceNetworkConstants.Command] = NetCmdRequest
-        };
-
-        component.TargetList.Clear();
-        _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device);
-    }
-
-    /// <summary>
-    /// Gets called when the units tag got updated
-    /// </summary>
-    private void OnConfigurationUpdated(EntityUid uid, MailingUnitComponent component, ConfigurationSystem.ConfigurationUpdatedEvent args)
-    {
-        var configuration = args.Configuration.Config;
-        if (!configuration.ContainsKey(TagConfigurationKey) || configuration[TagConfigurationKey] == string.Empty)
-        {
-            component.Tag = null;
-            return;
-        }
-
-        component.Tag = configuration[TagConfigurationKey];
-        UpdateUserInterface(uid, component);
-    }
-
-    private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args)
-    {
-        if (args.Handled || !args.Complex)
-            return;
-
-        if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
-        {
-            return;
-        }
-
-        args.Handled = true;
-        UpdateTargetList(uid, component);
-        _userInterfaceSystem.OpenUi(uid, MailingUnitUiKey.Key, actor.PlayerSession);
-    }
-
-    /// <summary>
-    /// Gets called when the disposal unit components ui state changes. This is required because the mailing unit requires a disposal unit component and overrides its ui
-    /// </summary>
-    private void OnDisposalUnitUIStateChange(EntityUid uid, MailingUnitComponent component, DisposalUnitUIStateUpdatedEvent args)
-    {
-        component.DisposalUnitInterfaceState = args.State;
-        UpdateUserInterface(uid, component);
-    }
-
-    private void UpdateUserInterface(EntityUid uid, MailingUnitComponent component)
-    {
-        if (component.DisposalUnitInterfaceState == null)
-            return;
-
-        var state = new MailingUnitBoundUserInterfaceState(component.DisposalUnitInterfaceState, component.Target, component.TargetList.ShallowClone(), component.Tag);
-        _userInterfaceSystem.SetUiState(uid, MailingUnitUiKey.Key, state);
-    }
-
-    private void OnTargetSelected(EntityUid uid, MailingUnitComponent component, TargetSelectedMessage args)
-    {
-        component.Target = args.Target;
-        UpdateUserInterface(uid, component);
-    }
-
-    /// <summary>
-    /// Checks if the unit is powered if an <see cref="ApcPowerReceiverComponent"/> is present
-    /// </summary>
-    /// <returns>True if the power receiver component is powered or not present</returns>
-    private bool IsPowered(EntityUid uid, ApcPowerReceiverComponent? powerReceiver = null)
-    {
-        if (Resolve(uid, ref powerReceiver) && !powerReceiver.Powered)
-            return false;
-
-        return true;
-    }
 }
diff --git a/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs
deleted file mode 100644 (file)
index e4b8955..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-using Content.Server.Disposal.Unit.EntitySystems;
-
-namespace Content.Server.Disposal.Tube.Components
-{
-    [RegisterComponent]
-    [Access(typeof(DisposalTubeSystem), typeof(DisposalUnitSystem))]
-    public sealed partial class DisposalEntryComponent : Component
-    {
-        public const string HolderPrototypeId = "DisposalHolder";
-    }
-}
similarity index 70%
rename from Content.Server/Disposal/Tube/Components/DisposalBendComponent.cs
rename to Content.Server/Disposal/Tube/DisposalBendComponent.cs
index ace8a684621b9f26ba2ce83e649adb7be3d16cae..7aa48f4bd343e52c68e669d63ebf6fbd927cbc49 100644 (file)
@@ -1,4 +1,4 @@
-namespace Content.Server.Disposal.Tube.Components;
+namespace Content.Server.Disposal.Tube;
 
 [RegisterComponent]
 [Access(typeof(DisposalTubeSystem))]
similarity index 84%
rename from Content.Server/Disposal/Tube/Components/DisposalJunctionComponent.cs
rename to Content.Server/Disposal/Tube/DisposalJunctionComponent.cs
index d929dd16bf9c91ffbea5242946eb9881b6aa5424..af0d7eda68a904974eb4bbdefcc61a225d8c32b9 100644 (file)
@@ -1,4 +1,4 @@
-namespace Content.Server.Disposal.Tube.Components;
+namespace Content.Server.Disposal.Tube;
 
 [RegisterComponent]
 [Access(typeof(DisposalTubeSystem))]
similarity index 57%
rename from Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs
rename to Content.Server/Disposal/Tube/DisposalRouterComponent.cs
index 18c0945cef1717c3a82631332e06f129cbf093ae..2446cf27f72fe9cda3c38b8055c2902e8c7407c3 100644 (file)
@@ -1,12 +1,6 @@
-using Content.Server.UserInterface;
-using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
-using Robust.Shared.Physics;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Player;
-using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent;
 
-namespace Content.Server.Disposal.Tube.Components
+namespace Content.Server.Disposal.Tube
 {
     [RegisterComponent]
     [Access(typeof(DisposalTubeSystem))]
similarity index 91%
rename from Content.Server/Disposal/Tube/Components/DisposalSignalRouterComponent.cs
rename to Content.Server/Disposal/Tube/DisposalSignalRouterComponent.cs
index b4ef81d8980174f041dbfee92c8f1f1e5708d9dc..bee8293f589da4335d0b4bb07ba39b5804b71025 100644 (file)
@@ -1,8 +1,7 @@
-using Content.Server.Disposal.Tube.Systems;
 using Content.Shared.DeviceLinking;
 using Robust.Shared.Prototypes;
 
-namespace Content.Server.Disposal.Tube.Components;
+namespace Content.Server.Disposal.Tube;
 
 /// <summary>
 /// Requires <see cref="DisposalJunctionComponent"/> to function.
similarity index 94%
rename from Content.Server/Disposal/Tube/Systems/DisposalSignalRouterSystem.cs
rename to Content.Server/Disposal/Tube/DisposalSignalRouterSystem.cs
index f1fdedb522674f27dc63e778ded444241798ffbe..0d6c03b8c50d64775d08160284616b57483ace87 100644 (file)
@@ -1,8 +1,7 @@
 using Content.Server.DeviceLinking.Systems;
-using Content.Server.Disposal.Tube.Components;
 using Content.Shared.DeviceLinking.Events;
 
-namespace Content.Server.Disposal.Tube.Systems;
+namespace Content.Server.Disposal.Tube;
 
 /// <summary>
 /// Handles signals and the routing get next direction event.
similarity index 53%
rename from Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs
rename to Content.Server/Disposal/Tube/DisposalTaggerComponent.cs
index a291f4a941a11ca981d64ac083c4587142eb09b1..3f072a80c7e627317788c4056379d2327ad2d837 100644 (file)
@@ -1,13 +1,6 @@
-using Content.Server.Disposal.Unit.Components;
-using Content.Server.UserInterface;
-using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
-using Robust.Shared.Physics;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Player;
-using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent;
 
-namespace Content.Server.Disposal.Tube.Components
+namespace Content.Server.Disposal.Tube
 {
     [RegisterComponent]
     public sealed partial class DisposalTaggerComponent : DisposalTransitComponent
similarity index 82%
rename from Content.Server/Disposal/Tube/Components/DisposalTransitComponent.cs
rename to Content.Server/Disposal/Tube/DisposalTransitComponent.cs
index e91632751634c633855104ad7edf56b094f1bddd..10bc7d5df799a7956055c5c48005bf5a45fb74dd 100644 (file)
@@ -1,4 +1,4 @@
-namespace Content.Server.Disposal.Tube.Components
+namespace Content.Server.Disposal.Tube
 {
     // TODO: Different types of tubes eject in random direction with no exit point
     [RegisterComponent]
similarity index 90%
rename from Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
rename to Content.Server/Disposal/Tube/DisposalTubeComponent.cs
index c16f1fcc228373efcd1f0a75351c12621ab67bfa..15d02ad1ef391e07665976435eb18de6e15fa59f 100644 (file)
@@ -1,9 +1,9 @@
-using Content.Server.Disposal.Unit.EntitySystems;
+using Content.Server.Disposal.Unit;
 using Content.Shared.Damage;
 using Robust.Shared.Audio;
 using Robust.Shared.Containers;
 
-namespace Content.Server.Disposal.Tube.Components;
+namespace Content.Server.Disposal.Tube;
 
 [RegisterComponent]
 [Access(typeof(DisposalTubeSystem), typeof(DisposableSystem))]
index 20626a5eeeb3aecfee46df931e660b62d1c69896..f1e094db20558a5ddc7f9867557814ab6842b59d 100644 (file)
@@ -2,12 +2,12 @@ using System.Linq;
 using System.Text;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Construction.Completions;
-using Content.Server.Disposal.Tube.Components;
-using Content.Server.Disposal.Unit.Components;
-using Content.Server.Disposal.Unit.EntitySystems;
+using Content.Server.Disposal.Unit;
 using Content.Server.Popups;
 using Content.Shared.Destructible;
 using Content.Shared.Disposal.Components;
+using Content.Shared.Disposal.Tube;
+using Content.Shared.Disposal.Unit;
 using Robust.Server.GameObjects;
 using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
@@ -16,12 +16,10 @@ using Robust.Shared.Map.Components;
 using Robust.Shared.Physics;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Random;
-using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent;
-using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent;
 
 namespace Content.Server.Disposal.Tube
 {
-    public sealed class DisposalTubeSystem : EntitySystem
+    public sealed class DisposalTubeSystem : SharedDisposalTubeSystem
     {
         [Dependency] private readonly IRobustRandom _random = default!;
         [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
@@ -49,8 +47,8 @@ namespace Content.Server.Disposal.Tube
             SubscribeLocalEvent<DisposalBendComponent, GetDisposalsConnectableDirectionsEvent>(OnGetBendConnectableDirections);
             SubscribeLocalEvent<DisposalBendComponent, GetDisposalsNextDirectionEvent>(OnGetBendNextDirection);
 
-            SubscribeLocalEvent<DisposalEntryComponent, GetDisposalsConnectableDirectionsEvent>(OnGetEntryConnectableDirections);
-            SubscribeLocalEvent<DisposalEntryComponent, GetDisposalsNextDirectionEvent>(OnGetEntryNextDirection);
+            SubscribeLocalEvent<Shared.Disposal.Tube.DisposalEntryComponent, GetDisposalsConnectableDirectionsEvent>(OnGetEntryConnectableDirections);
+            SubscribeLocalEvent<Shared.Disposal.Tube.DisposalEntryComponent, GetDisposalsNextDirectionEvent>(OnGetEntryNextDirection);
 
             SubscribeLocalEvent<DisposalJunctionComponent, GetDisposalsConnectableDirectionsEvent>(OnGetJunctionConnectableDirections);
             SubscribeLocalEvent<DisposalJunctionComponent, GetDisposalsNextDirectionEvent>(OnGetJunctionNextDirection);
@@ -64,13 +62,13 @@ namespace Content.Server.Disposal.Tube
             SubscribeLocalEvent<DisposalTaggerComponent, GetDisposalsConnectableDirectionsEvent>(OnGetTaggerConnectableDirections);
             SubscribeLocalEvent<DisposalTaggerComponent, GetDisposalsNextDirectionEvent>(OnGetTaggerNextDirection);
 
-            Subs.BuiEvents<DisposalRouterComponent>(DisposalRouterUiKey.Key, subs =>
+            Subs.BuiEvents<DisposalRouterComponent>(SharedDisposalRouterComponent.DisposalRouterUiKey.Key, subs =>
             {
                 subs.Event<BoundUIOpenedEvent>(OnOpenRouterUI);
                 subs.Event<SharedDisposalRouterComponent.UiActionMessage>(OnUiAction);
             });
 
-            Subs.BuiEvents<DisposalTaggerComponent>(DisposalTaggerUiKey.Key, subs =>
+            Subs.BuiEvents<DisposalTaggerComponent>(SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key, subs =>
             {
                 subs.Event<BoundUIOpenedEvent>(OnOpenTaggerUI);
                 subs.Event<SharedDisposalTaggerComponent.UiActionMessage>(OnUiAction);
@@ -161,12 +159,12 @@ namespace Content.Server.Disposal.Tube
             args.Next = previousDF == ev.Connectable[0] ? ev.Connectable[1] : ev.Connectable[0];
         }
 
-        private void OnGetEntryConnectableDirections(EntityUid uid, DisposalEntryComponent component, ref GetDisposalsConnectableDirectionsEvent args)
+        private void OnGetEntryConnectableDirections(EntityUid uid, Shared.Disposal.Tube.DisposalEntryComponent component, ref GetDisposalsConnectableDirectionsEvent args)
         {
             args.Connectable = new[] { Transform(uid).LocalRotation.GetDir() };
         }
 
-        private void OnGetEntryNextDirection(EntityUid uid, DisposalEntryComponent component, ref GetDisposalsNextDirectionEvent args)
+        private void OnGetEntryNextDirection(EntityUid uid, Shared.Disposal.Tube.DisposalEntryComponent component, ref GetDisposalsNextDirectionEvent args)
         {
             // Ejects contents when they come from the same direction the entry is facing.
             if (args.Holder.PreviousDirectionFrom != Direction.Invalid)
@@ -283,10 +281,10 @@ namespace Content.Server.Disposal.Tube
 
         private void OnOpenTaggerUI(EntityUid uid, DisposalTaggerComponent tagger, BoundUIOpenedEvent args)
         {
-            if (_uiSystem.HasUi(uid, DisposalTaggerUiKey.Key))
+            if (_uiSystem.HasUi(uid, SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key))
             {
-                _uiSystem.SetUiState(uid, DisposalTaggerUiKey.Key,
-                    new DisposalTaggerUserInterfaceState(tagger.Tag));
+                _uiSystem.SetUiState(uid, SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key,
+                    new SharedDisposalTaggerComponent.DisposalTaggerUserInterfaceState(tagger.Tag));
             }
         }
 
@@ -298,7 +296,7 @@ namespace Content.Server.Disposal.Tube
         {
             if (router.Tags.Count <= 0)
             {
-                _uiSystem.SetUiState(uid, DisposalRouterUiKey.Key, new DisposalRouterUserInterfaceState(""));
+                _uiSystem.SetUiState(uid, SharedDisposalRouterComponent.DisposalRouterUiKey.Key, new SharedDisposalRouterComponent.DisposalRouterUserInterfaceState(""));
                 return;
             }
 
@@ -312,7 +310,7 @@ namespace Content.Server.Disposal.Tube
 
             taglist.Remove(taglist.Length - 2, 2);
 
-            _uiSystem.SetUiState(uid, DisposalRouterUiKey.Key, new DisposalRouterUserInterfaceState(taglist.ToString()));
+            _uiSystem.SetUiState(uid, SharedDisposalRouterComponent.DisposalRouterUiKey.Key, new SharedDisposalRouterComponent.DisposalRouterUserInterfaceState(taglist.ToString()));
         }
 
         private void OnAnchorChange(EntityUid uid, DisposalTubeComponent component, ref AnchorStateChangedEvent args)
@@ -419,13 +417,13 @@ namespace Content.Server.Disposal.Tube
             _popups.PopupEntity(Loc.GetString("disposal-tube-component-popup-directions-text", ("directions", directions)), tubeId, recipient);
         }
 
-        public bool TryInsert(EntityUid uid, DisposalUnitComponent from, IEnumerable<string>? tags = default, DisposalEntryComponent? entry = null)
+        public override bool TryInsert(EntityUid uid, DisposalUnitComponent from, IEnumerable<string>? tags = default, DisposalEntryComponent? entry = null)
         {
             if (!Resolve(uid, ref entry))
                 return false;
 
             var xform = Transform(uid);
-            var holder = Spawn(DisposalEntryComponent.HolderPrototypeId, _transform.GetMapCoordinates(uid, xform: xform));
+            var holder = Spawn(entry.HolderPrototypeId, _transform.GetMapCoordinates(uid, xform: xform));
             var holderComponent = Comp<DisposalHolderComponent>(holder);
 
             foreach (var entity in from.Container.ContainedEntities.ToArray())
@@ -436,7 +434,7 @@ namespace Content.Server.Disposal.Tube
             _atmosSystem.Merge(holderComponent.Air, from.Air);
             from.Air.Clear();
 
-            if (tags != default)
+            if (tags != null)
                 holderComponent.Tags.UnionWith(tags);
 
             return _disposableSystem.EnterTube(holder, uid, holderComponent);
index 2872a0e6d0654b8b71240a1feaee54fdc90293bf..30dd1c3769a116573d1decb1ef2402ba2fb4bd69 100644 (file)
@@ -1,4 +1,4 @@
-using Content.Server.Disposal.Unit.Components;
+using Content.Server.Disposal.Unit;
 
 namespace Content.Server.Disposal.Tube;
 
index 564c46be7a0d25b83115a17919971e7156494f32..6719a9a2ab1c46699e34b30fabf8bb95fc4c509d 100644 (file)
@@ -1,6 +1,5 @@
 using Content.Server.Administration;
 using Content.Server.Disposal.Tube;
-using Content.Server.Disposal.Tube.Components;
 using Content.Shared.Administration;
 using Robust.Shared.Console;
 
similarity index 81%
rename from Content.Server/Disposal/Unit/Components/BeingDisposedComponent.cs
rename to Content.Server/Disposal/Unit/BeingDisposedComponent.cs
index 060c7c98bd062f2f20c56bf412913a02ff21ea78..3cff669855ef7f3bfa196698eee1d14015b41f98 100644 (file)
@@ -1,4 +1,4 @@
-namespace Content.Server.Disposal.Unit.Components;
+namespace Content.Server.Disposal.Unit;
 
 /// <summary>
 ///     A component added to entities that are currently in disposals.
similarity index 92%
rename from Content.Server/Disposal/Unit/EntitySystems/BeingDisposedSystem.cs
rename to Content.Server/Disposal/Unit/BeingDisposedSystem.cs
index 6fbfb1523a1a562b828861ac329e50ba5f980446..fcff4ba3b55087ec4d52f3ea422252d56b97e488 100644 (file)
@@ -1,8 +1,7 @@
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Body.Systems;
-using Content.Server.Disposal.Unit.Components;
 
-namespace Content.Server.Disposal.Unit.EntitySystems;
+namespace Content.Server.Disposal.Unit;
 
 public sealed class BeingDisposedSystem : EntitySystem
 {
diff --git a/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs b/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs
deleted file mode 100644 (file)
index 548af03..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-using Content.Server.Atmos;
-using Content.Shared.Atmos;
-using Content.Shared.Disposal.Components;
-
-namespace Content.Server.Disposal.Unit.Components;
-
-// GasMixture life.
-[RegisterComponent]
-public sealed partial class DisposalUnitComponent : SharedDisposalUnitComponent
-{
-    [DataField("air")]
-    public GasMixture Air = new(Atmospherics.CellVolume);
-}
similarity index 98%
rename from Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs
rename to Content.Server/Disposal/Unit/DisposableSystem.cs
index 0e624ca6f5a6f7b82897dad089930b0819928e10..a94176246c1df3bdbebaf7677164625aba91dc25 100644 (file)
@@ -1,9 +1,8 @@
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Disposal.Tube;
-using Content.Server.Disposal.Tube.Components;
-using Content.Server.Disposal.Unit.Components;
 using Content.Shared.Body.Components;
 using Content.Shared.Damage;
+using Content.Shared.Disposal.Components;
 using Content.Shared.Item;
 using Content.Shared.Throwing;
 using Robust.Shared.Audio.Systems;
@@ -12,7 +11,7 @@ using Robust.Shared.Map.Components;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Physics.Systems;
 
-namespace Content.Server.Disposal.Unit.EntitySystems
+namespace Content.Server.Disposal.Unit
 {
     public sealed class DisposableSystem : EntitySystem
     {
similarity index 94%
rename from Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs
rename to Content.Server/Disposal/Unit/DisposalHolderComponent.cs
index 690b33968b0f3a07369d929f9f390e1a24e5839b..be90ae9f9ccfa7f75f4e680ed56ef48f6f9433b5 100644 (file)
@@ -1,9 +1,8 @@
 using Content.Server.Atmos;
-using Content.Server.Disposal.Tube.Components;
 using Content.Shared.Atmos;
 using Robust.Shared.Containers;
 
-namespace Content.Server.Disposal.Unit.Components
+namespace Content.Server.Disposal.Unit
 {
     [RegisterComponent]
     public sealed partial class DisposalHolderComponent : Component, IGasMixtureHolder
diff --git a/Content.Server/Disposal/Unit/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/DisposalUnitSystem.cs
new file mode 100644 (file)
index 0000000..4c6436c
--- /dev/null
@@ -0,0 +1,44 @@
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Atmos;
+using Content.Shared.Destructible;
+using Content.Shared.Disposal.Components;
+using Content.Shared.Disposal.Unit;
+using Content.Shared.Explosion;
+
+namespace Content.Server.Disposal.Unit;
+
+public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
+{
+    [Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<DisposalUnitComponent, DestructionEventArgs>(OnDestruction);
+        SubscribeLocalEvent<DisposalUnitComponent, BeforeExplodeEvent>(OnExploded);
+    }
+
+    protected override void HandleAir(EntityUid uid, DisposalUnitComponent component, TransformComponent xform)
+    {
+        var air = component.Air;
+        var indices = TransformSystem.GetGridTilePositionOrDefault((uid, xform));
+
+        if (_atmosSystem.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is { Temperature: > 0f } environment)
+        {
+            var transferMoles = 0.1f * (0.25f * Atmospherics.OneAtmosphere * 1.01f - air.Pressure) * air.Volume / (environment.Temperature * Atmospherics.R);
+
+            component.Air = environment.Remove(transferMoles);
+        }
+    }
+
+    private void OnDestruction(EntityUid uid, DisposalUnitComponent component, DestructionEventArgs args)
+    {
+        TryEjectContents(uid, component);
+    }
+
+    private void OnExploded(Entity<DisposalUnitComponent> ent, ref BeforeExplodeEvent args)
+    {
+        args.Contents.AddRange(ent.Comp.Container.ContainedEntities);
+    }
+}
similarity index 65%
rename from Content.Server/Disposal/Unit/EntitySystems/DoInsertDisposalUnitEvent.cs
rename to Content.Server/Disposal/Unit/DoInsertDisposalUnitEvent.cs
index 6fdfce61da60ea7578aa62e2df0404792c774688..3f3fee978544e95be86bbc85185ebfce594da5f4 100644 (file)
@@ -1,4 +1,4 @@
-namespace Content.Server.Disposal.Unit.EntitySystems
+namespace Content.Server.Disposal.Unit
 {
     public record DoInsertDisposalUnitEvent(EntityUid? User, EntityUid ToInsert, EntityUid Unit);
 }
index fe6b03fab7ba5b2d8a2f3b3f6bd95029231a3a93..2921bd5ef6c1ea3fad5e9b046cf164afa273e60d 100644 (file)
@@ -7,6 +7,7 @@ using Content.Shared.Fax;
 using Content.Shared.Follower;
 using Content.Shared.Ghost;
 using Content.Shared.Paper;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Fax.AdminUI;
 
index 1ac7bd23cafebe7a3fdc807905175fdc72670529..79710e3f9786d06041cbc4f6bf00c366d4226260 100644 (file)
@@ -1,8 +1,6 @@
 using Content.Server.Administration;
 using Content.Server.Administration.Managers;
 using Content.Server.Chat.Managers;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Popups;
 using Content.Server.Power.Components;
@@ -12,7 +10,7 @@ using Content.Shared.Administration.Logs;
 using Content.Shared.Containers.ItemSlots;
 using Content.Shared.Database;
 using Content.Shared.DeviceNetwork;
-using Content.Shared.Emag.Components;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Emag.Systems;
 using Content.Shared.Fax;
 using Content.Shared.Fax.Systems;
@@ -27,9 +25,9 @@ using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
-using Robust.Shared.Prototypes;
 using Content.Shared.NameModifier.Components;
 using Content.Shared.Power;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Fax;
 
index 53c60296d520e644d26744438bd0bcf0b6f15a0d..b98ba156231716759a7d6e47bbcca3aedbafeb3c 100644 (file)
@@ -19,6 +19,8 @@ using Robust.Shared.Timing;
 using Robust.Shared.Audio.Systems;
 using Content.Shared.Damage.Systems;
 using Content.Shared.Damage.Components;
+using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Power;
 
 namespace Content.Server.Light.EntitySystems
index a53df6dbae9f038d78252ccc7b14f4cd37aa1292..0e1d27a3c55621d701bab91e6b3bcdce69da36de 100644 (file)
@@ -2,6 +2,8 @@ using System.Linq;
 using Content.Server.DeviceNetwork;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.PowerCell;
+using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Medical.CrewMonitoring;
 using Content.Shared.Medical.SuitSensor;
 using Content.Shared.Pinpointer;
index d7b8cc67a50e00f65bf36fc0147b4e315ff9d060..ad50a8f957ba9643dfeffca38ca22479b7bcbdd7 100644 (file)
@@ -1,10 +1,10 @@
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Medical.SuitSensors;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Medical.SuitSensor;
 using Robust.Shared.Timing;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Medical.CrewMonitoring;
 
index fa4344cc78b239fe7107dc8f25236735e01d112c..2ab09e746fbd8f97a315ca342ad0cdb8c55d6b80 100644 (file)
@@ -1,7 +1,5 @@
 using System.Numerics;
 using Content.Server.Access.Systems;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Emp;
 using Content.Server.Medical.CrewMonitoring;
@@ -26,6 +24,7 @@ using Robust.Shared.Map;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using Robust.Shared.Timing;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Medical.SuitSensors;
 
index a9527020b07d38051ddcd6b4e4e07d9dc13acac9..bdf688efe733f3bf27f5c5a889a26240797bf643 100644 (file)
@@ -2,27 +2,23 @@ using Content.Server.Access.Systems;
 using Content.Server.AlertLevel;
 using Content.Server.CartridgeLoader;
 using Content.Server.Chat.Managers;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.Instruments;
-using Content.Server.Light.EntitySystems;
 using Content.Server.PDA.Ringer;
 using Content.Server.Station.Systems;
-using Content.Server.Store.Components;
 using Content.Server.Store.Systems;
 using Content.Server.Traitor.Uplink;
 using Content.Shared.Access.Components;
 using Content.Shared.CartridgeLoader;
 using Content.Shared.Chat;
 using Content.Shared.Light;
-using Content.Shared.Light.Components;
 using Content.Shared.Light.EntitySystems;
 using Content.Shared.PDA;
-using Content.Shared.Store.Components;
 using Robust.Server.Containers;
 using Robust.Server.GameObjects;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
 using Robust.Shared.Utility;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.PDA
 {
index 023927345506a9f8424fa91cf66f7abd2ec5c4d3..f3405486e6754c7d397a0e914534f8ef7254a65c 100644 (file)
@@ -15,7 +15,6 @@ namespace Content.Server.Power.EntitySystems
     public sealed class PowerReceiverSystem : SharedPowerReceiverSystem
     {
         [Dependency] private readonly IAdminManager _adminManager = default!;
-
         private EntityQuery<ApcPowerReceiverComponent> _recQuery;
         private EntityQuery<ApcPowerProviderComponent> _provQuery;
 
index 437d805dcd8ea361642434cde66e27636690f9f5..04f876c2c283158ec21f7467a538a8a9a1b6111f 100644 (file)
@@ -9,6 +9,7 @@ using Content.Server.NodeContainer.Nodes;
 using Content.Server.Power.Components;
 using Content.Shared.Atmos;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Examine;
 using Content.Shared.Power;
 using Content.Shared.Power.EntitySystems;
index 1fe48d22b4561c2a8471737d6da4188a1e74559f..1cea981d3c8d086dd8c77d8eb893262c6f650a65 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.Power.EntitySystems;
 using Content.Server.PowerCell;
 using Content.Shared.DeviceNetwork.Components;
index 916694fdd86d1bbf37432d41e93008c511de2134..c4554d65d673fa411b162e2ddbbdb81b1214f76e 100644 (file)
@@ -1,5 +1,4 @@
 using Content.Server.Administration.Logs;
-using Content.Server.DeviceNetwork;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Radio.EntitySystems;
 using Content.Shared.Lock;
@@ -10,7 +9,7 @@ using Content.Shared.Robotics.Components;
 using Content.Shared.Robotics.Systems;
 using Robust.Server.GameObjects;
 using Robust.Shared.Timing;
-using System.Diagnostics.CodeAnalysis;
+using Content.Shared.DeviceNetwork.Events;
 
 namespace Content.Server.Research.Systems;
 
index bb5934f3f08d1257ba2919624009bf9e4cdd122b..900a52057e65fc1459e9fb28f6cfa82c136bfe54 100644 (file)
@@ -4,8 +4,6 @@ using Content.Server.AlertLevel;
 using Content.Shared.CCVar;
 using Content.Server.Chat.Managers;
 using Content.Server.Chat.Systems;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.GameTicking;
 using Content.Server.Screens.Components;
@@ -21,6 +19,7 @@ using Robust.Shared.Configuration;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
+using Content.Shared.DeviceNetwork.Components;
 using Timer = Robust.Shared.Timing.Timer;
 
 namespace Content.Server.RoundEnd
index 782fe38c8884dc840079a46d866e3e0cf4ee7367..c159bfe1d5033036e925f5d2dec551103aba4c37 100644 (file)
@@ -2,6 +2,8 @@ using Content.Shared.TextScreen;
 using Content.Server.Screens.Components;
 using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
+using Content.Shared.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
 using Robust.Shared.Timing;
 
 
@@ -63,7 +65,7 @@ public sealed class ScreenSystem : EntitySystem
     /// <summary>
     /// Determines if/how a timer packet affects this screen.
     /// Currently there are 2 broadcast domains: Arrivals, and every other screen.
-    /// Domain is determined by the <see cref="DeviceNetworkComponent.TransmitFrequencyId"/> on each timer.
+    /// Domain is determined by the <see cref="Shared.DeviceNetwork.Components.DeviceNetworkComponent.TransmitFrequencyId"/> on each timer.
     /// Each broadcast domain is divided into subnets. Screen MapUid determines subnet.
     /// Subnets are the shuttle, source, and dest. Source/dest change each jump.
     /// This is required to send different timers to the shuttle/terminal/station.
index 501b094c892455f85639733a837ed6bca852fb2f..5047cd1d297f0c6283dd53aba2e63331626df51d 100644 (file)
@@ -2,6 +2,7 @@
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Power.Components;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 
 namespace Content.Server.SensorMonitoring;
 
index dec3e6c36ead2820e07c106e77a797f0cd0ac539..a562abd926cfb280dfe56e54fbbd671232d30b4f 100644 (file)
@@ -1,7 +1,7 @@
-using Content.Server.DeviceNetwork.Components;
-using Content.Shared.SensorMonitoring;
+using Content.Shared.SensorMonitoring;
 using Robust.Shared.Collections;
 using ConsoleUIState = Content.Shared.SensorMonitoring.SensorMonitoringConsoleBoundInterfaceState;
+using Content.Shared.DeviceNetwork.Components;
 using IncrementalUIState = Content.Shared.SensorMonitoring.SensorMonitoringIncrementalUpdate;
 
 namespace Content.Server.SensorMonitoring;
index ddd7812394ee05c003b6e150d908a84a512ac0b2..ebe8f304bab418a7f0bdb6f23c716c334841ce6e 100644 (file)
@@ -1,10 +1,7 @@
 using Content.Server.Atmos.Monitor.Components;
 using Content.Server.Atmos.Monitor.Systems;
-using Content.Server.Atmos.Piping.Binary.Components;
 using Content.Server.Atmos.Piping.Components;
 using Content.Server.Atmos.Piping.Unary.Components;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Power.Generation.Teg;
 using Content.Shared.Atmos.Monitor;
@@ -12,6 +9,7 @@ using Content.Shared.Atmos.Piping.Binary.Components;
 using Content.Shared.Atmos.Piping.Unary.Components;
 using Content.Shared.DeviceNetwork;
 using Content.Shared.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.DeviceNetwork.Systems;
 using Content.Shared.SensorMonitoring;
 using Robust.Server.GameObjects;
index 896570fb346e0b1a316c73cedd3b0a4278a4f57b..aa1c2e6dff105134303eed9d98c177bc97b2500f 100644 (file)
@@ -2,7 +2,6 @@ using System.Linq;
 using System.Numerics;
 using Content.Server.Administration;
 using Content.Server.Chat.Managers;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.GameTicking;
 using Content.Server.GameTicking.Events;
@@ -19,6 +18,7 @@ using Content.Shared.Administration;
 using Content.Shared.CCVar;
 using Content.Shared.Damage.Components;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Components;
 using Content.Shared.GameTicking;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Movement.Components;
@@ -26,20 +26,17 @@ using Content.Shared.Parallax.Biomes;
 using Content.Shared.Salvage;
 using Content.Shared.Shuttles.Components;
 using Content.Shared.Tiles;
-using Robust.Server.GameObjects;
 using Robust.Shared.Collections;
 using Robust.Shared.Configuration;
 using Robust.Shared.Console;
-using Robust.Shared.EntitySerialization;
 using Robust.Shared.EntitySerialization.Systems;
 using Robust.Shared.Map;
-using Robust.Shared.Map.Components;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
+using Robust.Shared.Spawners;
 using Robust.Shared.Timing;
 using Robust.Shared.Utility;
-using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
 
 namespace Content.Server.Shuttles.Systems;
 
index 09aad1d931add01a40c46ac78edd312652c08376..95c6ab5a1ba95be76889064345ed72a841d2740d 100644 (file)
@@ -1,5 +1,4 @@
 using System.Threading;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.Screens.Components;
 using Content.Server.Shuttles.Components;
 using Content.Server.Shuttles.Events;
@@ -14,6 +13,7 @@ using Content.Shared.Shuttles.Systems;
 using Content.Shared.UserInterface;
 using Robust.Shared.Map;
 using Robust.Shared.Player;
+using Content.Shared.DeviceNetwork.Components;
 using Timer = Robust.Shared.Timing.Timer;
 
 namespace Content.Server.Shuttles.Systems;
index afa77421bd71590ad1d84fce10f3674238eb58d7..8e3e01bfb64bd0801af0aac531d83b4c827436f1 100644 (file)
@@ -6,7 +6,6 @@ using Content.Server.Administration.Logs;
 using Content.Server.Administration.Managers;
 using Content.Server.Chat.Systems;
 using Content.Server.Communications;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.GameTicking.Events;
 using Content.Server.Pinpointer;
@@ -37,6 +36,7 @@ using Robust.Shared.Player;
 using Robust.Shared.Random;
 using Robust.Shared.Timing;
 using Robust.Shared.Utility;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.Shuttles.Systems;
 
index 4d2a8912e830c870f2aeb284b1878ce7183de847..e950d3f288742b81b061b3a4255df53ba52b1a13 100644 (file)
@@ -1,13 +1,10 @@
 using Content.Shared.DeviceNetwork;
-using Content.Shared.Emag.Components;
 using Content.Shared.Movement.Components;
 using Content.Shared.Popups;
 using Content.Shared.Robotics;
 using Content.Shared.Silicons.Borgs.Components;
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
-using Content.Server.DeviceNetwork.Systems;
-using Content.Server.Explosion.Components;
+using Content.Shared.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Emag.Systems;
 using Robust.Shared.Utility;
 
index 21e71c43168491c79778c75cb603325eff889e08..30bbb2f0e9b6fa9304ab45fcf9d5a067d2ec99e3 100644 (file)
@@ -3,6 +3,7 @@ using Content.Server.DeviceNetwork;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Power.Components;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Power;
 using Content.Shared.UserInterface;
 using Content.Shared.SurveillanceCamera;
index 315273a0cc4734c116417acd66f43c92dfd6d9dd..c6886dee338d7231be26930d1f7f120a3dc3a352 100644 (file)
@@ -1,15 +1,13 @@
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
-using Content.Server.Power.Components;
 using Content.Shared.ActionBlocker;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Power;
 using Content.Shared.SurveillanceCamera;
 using Content.Shared.Verbs;
 using Robust.Server.GameObjects;
-using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.SurveillanceCamera;
 
index f1d1b58bf57c24cd16144e38345624cc441da8c6..709e383c06c1eaff42bd8f84e386f0801bccf0a4 100644 (file)
@@ -1,16 +1,15 @@
-using Content.Server.DeviceNetwork;
-using Content.Server.DeviceNetwork.Components;
 using Content.Server.DeviceNetwork.Systems;
 using Content.Server.Emp;
-using Content.Server.Power.Components;
 using Content.Shared.ActionBlocker;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Power;
 using Content.Shared.SurveillanceCamera;
 using Content.Shared.Verbs;
 using Robust.Server.GameObjects;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
+using Content.Shared.DeviceNetwork.Components;
 
 namespace Content.Server.SurveillanceCamera;
 
index 359d91fd1dc592bf72cff0db184d64d2db839541..72c011bc9059f6852b7b0acce51493a8805084e2 100644 (file)
@@ -8,6 +8,8 @@ using Content.Server.Power.Components;
 using Content.Server.Repairable;
 using Content.Shared.Destructible;
 using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
 using Content.Shared.Power;
 using Content.Shared.Turrets;
 using Content.Shared.Weapons.Ranged.Events;
index 1a924e5c3052a3442d8e854da4f6f5e18d35e838..22a42dea78038bdf86348739e2aeca278d8274b6 100644 (file)
@@ -13,7 +13,13 @@ namespace Content.Shared.Climbing.Components
         /// <summary>
         ///     The range from which this entity can be climbed.
         /// </summary>
-        [DataField("range")] public float Range = SharedInteractionSystem.InteractionRange / 1.4f;
+        [DataField] public float Range = SharedInteractionSystem.InteractionRange;
+
+        /// <summary>
+        /// Can drag-drop / verb vaulting be done? Set to false if climbing is being handled manually.
+        /// </summary>
+        [DataField]
+        public bool Vaultable = true;
 
         /// <summary>
         ///     The time it takes to climb onto the entity.
index f8f2ffda88c0b32b54e4541f60c058fedf4419d5..d7d9df7fdd9bc133d257b1b2039a0173c164790c 100644 (file)
@@ -149,7 +149,7 @@ public sealed partial class ClimbSystem : VirtualController
 
     private void OnCanDragDropOn(EntityUid uid, ClimbableComponent component, ref CanDropTargetEvent args)
     {
-        if (args.Handled)
+        if (args.Handled || !component.Vaultable)
             return;
 
         // If already climbing then don't show outlines.
@@ -261,7 +261,7 @@ public sealed partial class ClimbSystem : VirtualController
         args.Handled = true;
     }
 
-    private void Climb(EntityUid uid, EntityUid user, EntityUid climbable, bool silent = false, ClimbingComponent? climbing = null,
+    public void Climb(EntityUid uid, EntityUid user, EntityUid climbable, bool silent = false, ClimbingComponent? climbing = null,
         PhysicsComponent? physics = null, FixturesComponent? fixtures = null, ClimbableComponent? comp = null)
     {
         if (!Resolve(uid, ref climbing, ref physics, ref fixtures, false))
@@ -456,6 +456,12 @@ public sealed partial class ClimbSystem : VirtualController
     /// <param name="reason">The reason why it cant be dropped</param>
     public bool CanVault(ClimbableComponent component, EntityUid user, EntityUid target, out string reason)
     {
+        if (!component.Vaultable)
+        {
+            reason = string.Empty;
+            return false;
+        }
+
         if (!_actionBlockerSystem.CanInteract(user, target))
         {
             reason = Loc.GetString("comp-climbable-cant-interact");
index 621871af3cee5f65f0f833ed294ebbf144a6aa4f..63c084508393a7483656233ee515b55de6c06ca7 100644 (file)
@@ -2,34 +2,38 @@ using System.Text.RegularExpressions;
 using Content.Shared.Tools;
 using Content.Shared.Tools.Systems;
 using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 namespace Content.Shared.Configurable
 {
-    [RegisterComponent, NetworkedComponent]
+    /// <summary>
+    /// Configuration for mailing units.
+    /// </summary>
+    /// <remarks>
+    /// If you want a more detailed description ask the original coder.
+    /// </remarks>
+    [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
     public sealed partial class ConfigurationComponent : Component
     {
-        [DataField("config")]
+        /// <summary>
+        /// Tags for mail unit routing.
+        /// </summary>
+        [DataField, AutoNetworkedField]
         public Dictionary<string, string?> Config = new();
 
-        [DataField("qualityNeeded", customTypeSerializer: typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
-        public string QualityNeeded = SharedToolSystem.PulseQuality;
+        /// <summary>
+        /// Quality to open up the configuration UI.
+        /// </summary>
+        [DataField]
+        public ProtoId<ToolQualityPrototype> QualityNeeded = SharedToolSystem.PulseQuality;
 
-        [DataField("validation")]
+        /// <summary>
+        /// Validate tags in <see cref="Config"/>.
+        /// </summary>
+        [DataField]
         public Regex Validation = new("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled);
 
-        [Serializable, NetSerializable]
-        public sealed class ConfigurationBoundUserInterfaceState : BoundUserInterfaceState
-        {
-            public Dictionary<string, string?> Config { get; }
-
-            public ConfigurationBoundUserInterfaceState(Dictionary<string, string?> config)
-            {
-                Config = config;
-            }
-        }
-
         /// <summary>
         ///     Message data sent from client to server when the device configuration is updated.
         /// </summary>
diff --git a/Content.Shared/Configurable/SharedConfigurationSystem.cs b/Content.Shared/Configurable/SharedConfigurationSystem.cs
new file mode 100644 (file)
index 0000000..7049651
--- /dev/null
@@ -0,0 +1,77 @@
+using Content.Shared.Interaction;
+using Content.Shared.Tools.Systems;
+using Robust.Shared.Containers;
+using static Content.Shared.Configurable.ConfigurationComponent;
+
+namespace Content.Shared.Configurable;
+
+/// <summary>
+/// <see cref="ConfigurationComponent"/>
+/// </summary>
+public abstract class SharedConfigurationSystem : EntitySystem
+{
+    [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
+    [Dependency] private readonly SharedToolSystem _toolSystem = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ConfigurationComponent, ConfigurationUpdatedMessage>(OnUpdate);
+        SubscribeLocalEvent<ConfigurationComponent, InteractUsingEvent>(OnInteractUsing);
+        SubscribeLocalEvent<ConfigurationComponent, ContainerIsInsertingAttemptEvent>(OnInsert);
+    }
+
+    private void OnInteractUsing(EntityUid uid, ConfigurationComponent component, InteractUsingEvent args)
+    {
+        // TODO use activatable ui system
+        if (args.Handled)
+            return;
+
+        if (!_toolSystem.HasQuality(args.Used, component.QualityNeeded))
+            return;
+
+        args.Handled = _uiSystem.TryOpenUi(uid, ConfigurationUiKey.Key, args.User);
+    }
+
+    private void OnUpdate(EntityUid uid, ConfigurationComponent component, ConfigurationUpdatedMessage args)
+    {
+        foreach (var key in component.Config.Keys)
+        {
+            var value = args.Config.GetValueOrDefault(key);
+
+            if (string.IsNullOrWhiteSpace(value) || component.Validation != null && !component.Validation.IsMatch(value))
+                continue;
+
+            component.Config[key] = value;
+        }
+
+        Dirty(uid, component);
+        var updatedEvent = new ConfigurationUpdatedEvent(component);
+        RaiseLocalEvent(uid, updatedEvent);
+
+        // TODO support float (spinbox) and enum (drop-down) configurations
+        // TODO support verbs.
+    }
+
+    private void OnInsert(EntityUid uid, ConfigurationComponent component, ContainerIsInsertingAttemptEvent args)
+    {
+        if (!_toolSystem.HasQuality(args.EntityUid, component.QualityNeeded))
+            return;
+
+        args.Cancel();
+    }
+}
+
+/// <summary>
+/// Sent when configuration values got changes
+/// </summary>
+public sealed class ConfigurationUpdatedEvent : EntityEventArgs
+{
+    public ConfigurationComponent Configuration;
+
+    public ConfigurationUpdatedEvent(ConfigurationComponent configuration)
+    {
+        Configuration = configuration;
+    }
+}
diff --git a/Content.Shared/Containers/SharedThrowInsertContainerSystem.cs b/Content.Shared/Containers/SharedThrowInsertContainerSystem.cs
new file mode 100644 (file)
index 0000000..a5c300c
--- /dev/null
@@ -0,0 +1,8 @@
+namespace Content.Shared.Containers;
+
+/// <summary>
+/// Sent before the insertion is made.
+/// Allows preventing the insertion if any system on the entity should need to.
+/// </summary>
+[ByRefEvent]
+public record struct BeforeThrowInsertEvent(EntityUid ThrownEntity, bool Cancelled = false);
similarity index 93%
rename from Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs
rename to Content.Shared/DeviceNetwork/Components/DeviceNetworkComponent.cs
index 186da57e5d127fa98a6dcc051028432a87910948..37a86e7161ef566a8cf93012bfae579f51c4fb53 100644 (file)
@@ -1,11 +1,10 @@
-using Content.Server.DeviceNetwork.Systems;
-using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Systems;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
-namespace Content.Server.DeviceNetwork.Components
+namespace Content.Shared.DeviceNetwork.Components
 {
     [RegisterComponent]
-    [Access(typeof(DeviceNetworkSystem), typeof(DeviceNet))]
+    [Access(typeof(SharedDeviceNetworkSystem), typeof(DeviceNet))]
     public sealed partial class DeviceNetworkComponent : Component
     {
         public enum DeviceNetIdDefaults
@@ -113,14 +112,14 @@ namespace Content.Server.DeviceNetwork.Components
         ///     A list of device-lists that this device is on.
         /// </summary>
         [DataField]
-        [Access(typeof(DeviceListSystem))]
+        [Access(typeof(SharedDeviceListSystem))]
         public HashSet<EntityUid> DeviceLists = new();
 
         /// <summary>
         ///     A list of configurators that this device is on.
         /// </summary>
         [DataField]
-        [Access(typeof(NetworkConfiguratorSystem))]
+        [Access(typeof(SharedNetworkConfiguratorSystem))]
         public HashSet<EntityUid> Configurators = new();
     }
 }
similarity index 97%
rename from Content.Server/DeviceNetwork/DeviceNet.cs
rename to Content.Shared/DeviceNetwork/DeviceNet.cs
index c77d45c061b58d37553ee9a5aeba8eac690dbfa0..4234b30385c93a51105c94c7c824b4182e5c5260 100644 (file)
@@ -1,8 +1,7 @@
-using Content.Server.DeviceNetwork.Components;
 using Robust.Shared.Random;
-using static Content.Server.DeviceNetwork.Components.DeviceNetworkComponent;
+using Content.Shared.DeviceNetwork.Components;
 
-namespace Content.Server.DeviceNetwork;
+namespace Content.Shared.DeviceNetwork;
 
 /// <summary>
 ///     Data class for storing and retrieving information about devices connected to a device network.
similarity index 96%
rename from Content.Server/DeviceNetwork/DeviceNetworkConstants.cs
rename to Content.Shared/DeviceNetwork/DeviceNetworkConstants.cs
index 6cbad603b416557f0d0be9bcf9e88124d9f28b85..7fec72bb69518d6221b3f8047ecbc7ee42d7d45c 100644 (file)
@@ -1,7 +1,7 @@
-using Content.Server.DeviceNetwork.Components;
 using Robust.Shared.Utility;
+using Content.Shared.DeviceNetwork.Components;
 
-namespace Content.Server.DeviceNetwork
+namespace Content.Shared.DeviceNetwork
 {
     /// <summary>
     /// A collection of constants to help with using device networks
diff --git a/Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs b/Content.Shared/DeviceNetwork/Events/BeforeBroadcastAttemptEvent.cs
new file mode 100644 (file)
index 0000000..f495847
--- /dev/null
@@ -0,0 +1,17 @@
+using Content.Shared.DeviceNetwork.Components;
+
+namespace Content.Shared.DeviceNetwork.Events;
+
+/// <summary>
+/// Sent to the sending entity before broadcasting network packets to recipients
+/// </summary>
+public sealed class BeforeBroadcastAttemptEvent : CancellableEntityEventArgs
+{
+    public readonly IReadOnlySet<DeviceNetworkComponent> Recipients;
+    public HashSet<DeviceNetworkComponent>? ModifiedRecipients;
+
+    public BeforeBroadcastAttemptEvent(IReadOnlySet<DeviceNetworkComponent> recipients)
+    {
+        Recipients = recipients;
+    }
+}
diff --git a/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs b/Content.Shared/DeviceNetwork/Events/BeforePacketSentEvent.cs
new file mode 100644 (file)
index 0000000..5d5c038
--- /dev/null
@@ -0,0 +1,35 @@
+using System.Numerics;
+
+namespace Content.Shared.DeviceNetwork.Events;
+
+/// <summary>
+/// Event raised before a device network packet is send.
+/// Subscribed to by other systems to prevent the packet from being sent.
+/// </summary>
+public sealed class BeforePacketSentEvent : CancellableEntityEventArgs
+{
+    /// <summary>
+    /// The EntityUid of the entity the packet was sent from.
+    /// </summary>
+    public readonly EntityUid Sender;
+
+    public readonly TransformComponent SenderTransform;
+
+    /// <summary>
+    ///     The senders current position in world coordinates.
+    /// </summary>
+    public readonly Vector2 SenderPosition;
+
+    /// <summary>
+    /// The network the packet will be sent to.
+    /// </summary>
+    public readonly string NetworkId;
+
+    public BeforePacketSentEvent(EntityUid sender, TransformComponent xform, Vector2 senderPosition, string networkId)
+    {
+        Sender = sender;
+        SenderTransform = xform;
+        SenderPosition = senderPosition;
+        NetworkId = networkId;
+    }
+}
\ No newline at end of file
diff --git a/Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs b/Content.Shared/DeviceNetwork/Events/DeviceNetworkPacketEvent.cs
new file mode 100644 (file)
index 0000000..4ae6afe
--- /dev/null
@@ -0,0 +1,47 @@
+namespace Content.Shared.DeviceNetwork.Events;
+
+/// <summary>
+/// Event raised when a device network packet gets sent.
+/// </summary>
+public sealed class DeviceNetworkPacketEvent : EntityEventArgs
+{
+    /// <summary>
+    /// The id of the network that this packet is being sent on.
+    /// </summary>
+    public int NetId;
+
+    /// <summary>
+    /// The frequency the packet is sent on.
+    /// </summary>
+    public readonly uint Frequency;
+
+    /// <summary>
+    /// Address of the intended recipient. Null if the message was broadcast.
+    /// </summary>
+    public string? Address;
+
+    /// <summary>
+    /// The device network address of the sending entity.
+    /// </summary>
+    public readonly string SenderAddress;
+
+    /// <summary>
+    /// The entity that sent the packet.
+    /// </summary>
+    public EntityUid Sender;
+
+    /// <summary>
+    /// The data that is being sent.
+    /// </summary>
+    public readonly NetworkPayload Data;
+
+    public DeviceNetworkPacketEvent(int netId, string? address, uint frequency, string senderAddress, EntityUid sender, NetworkPayload data)
+    {
+        NetId = netId;
+        Address = address;
+        Frequency = frequency;
+        SenderAddress = senderAddress;
+        Sender = sender;
+        Data = data;
+    }
+}
\ No newline at end of file
diff --git a/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs b/Content.Shared/DeviceNetwork/Systems/SharedDeviceNetworkSystem.cs
new file mode 100644 (file)
index 0000000..5992c40
--- /dev/null
@@ -0,0 +1,25 @@
+using Content.Shared.DeviceNetwork.Components;
+
+namespace Content.Shared.DeviceNetwork.Systems;
+
+public abstract class SharedDeviceNetworkSystem : EntitySystem
+{
+    /// <summary>
+    /// Sends the given payload as a device network packet to the entity with the given address and frequency.
+    /// Addresses are given to the DeviceNetworkComponent of an entity when connecting.
+    /// </summary>
+    /// <param name="uid">The EntityUid of the sending entity</param>
+    /// <param name="address">The address of the entity that the packet gets sent to. If null, the message is broadcast to all devices on that frequency (except the sender)</param>
+    /// <param name="frequency">The frequency to send on</param>
+    /// <param name="data">The data to be sent</param>
+    /// <returns>Returns true when the packet was successfully enqueued.</returns>
+    public virtual bool QueuePacket(EntityUid uid,
+        string? address,
+        NetworkPayload data,
+        uint? frequency = null,
+        int? network = null,
+        DeviceNetworkComponent? device = null)
+    {
+        return false;
+    }
+}
similarity index 57%
rename from Content.Server/Disposal/Mailing/MailingUnitComponent.cs
rename to Content.Shared/Disposal/Mailing/MailingUnitComponent.cs
index be5eca99c49227991dac81a447f75a665f621c64..255d6cdf015be79f7ae6b546e25ab1e8d26e3ee4 100644 (file)
@@ -1,30 +1,28 @@
-using Content.Shared.Disposal.Components;
+using Content.Shared.Disposal.Mailing;
+using Robust.Shared.GameStates;
 
-namespace Content.Server.Disposal.Mailing;
+namespace Content.Shared.Disposal.Components;
 
-[Access(typeof(MailingUnitSystem))]
-[RegisterComponent]
+[Access(typeof(SharedMailingUnitSystem))]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
 public sealed partial class MailingUnitComponent : Component
 {
     /// <summary>
     /// List of targets the mailing unit can send to.
     /// Each target is just a disposal routing tag
     /// </summary>
-    [DataField("targetList")]
+    [DataField, AutoNetworkedField]
     public List<string> TargetList = new();
 
     /// <summary>
     /// The target that gets attached to the disposal holders tag list on flush
     /// </summary>
-    [DataField("target")]
+    [DataField, AutoNetworkedField]
     public string? Target;
 
     /// <summary>
     /// The tag for this mailing unit
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("tag")]
+    [DataField, AutoNetworkedField]
     public string? Tag;
-
-    public SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState? DisposalUnitInterfaceState;
 }
diff --git a/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs b/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs
new file mode 100644 (file)
index 0000000..cb7a8c4
--- /dev/null
@@ -0,0 +1,174 @@
+using Content.Shared.Configurable;
+using Content.Shared.DeviceNetwork;
+using Content.Shared.DeviceNetwork.Components;
+using Content.Shared.DeviceNetwork.Events;
+using Content.Shared.DeviceNetwork.Systems;
+using Content.Shared.Disposal.Components;
+using Content.Shared.Disposal.Unit;
+using Content.Shared.Disposal.Unit.Events;
+using Content.Shared.Interaction;
+using Content.Shared.Power.EntitySystems;
+using Robust.Shared.Player;
+
+namespace Content.Shared.Disposal.Mailing;
+
+public abstract class SharedMailingUnitSystem : EntitySystem
+{
+    [Dependency] private readonly SharedDeviceNetworkSystem _deviceNetworkSystem = default!;
+    [Dependency] private readonly SharedPowerReceiverSystem _power = default!;
+    [Dependency] protected readonly SharedUserInterfaceSystem UserInterfaceSystem = default!;
+
+    private const string MailTag = "mail";
+
+    private const string TagConfigurationKey = "tag";
+
+    private const string NetTag = "tag";
+    private const string NetSrc = "src";
+    private const string NetTarget = "target";
+    private const string NetCmdSent = "mail_sent";
+    private const string NetCmdRequest = "get_mailer_tag";
+    private const string NetCmdResponse = "mailer_tag";
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<MailingUnitComponent, ComponentInit>(OnComponentInit);
+        SubscribeLocalEvent<MailingUnitComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
+        SubscribeLocalEvent<MailingUnitComponent, BeforeDisposalFlushEvent>(OnBeforeFlush);
+        SubscribeLocalEvent<MailingUnitComponent, ConfigurationUpdatedEvent>(OnConfigurationUpdated);
+        SubscribeLocalEvent<MailingUnitComponent, ActivateInWorldEvent>(HandleActivate, before: new[] { typeof(SharedDisposalUnitSystem) });
+        SubscribeLocalEvent<MailingUnitComponent, TargetSelectedMessage>(OnTargetSelected);
+    }
+
+    private void OnComponentInit(EntityUid uid, MailingUnitComponent component, ComponentInit args)
+    {
+        UpdateTargetList(uid, component);
+    }
+
+    private void OnPacketReceived(EntityUid uid, MailingUnitComponent component, DeviceNetworkPacketEvent args)
+    {
+        if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || !_power.IsPowered(uid))
+            return;
+
+        switch (command)
+        {
+            case NetCmdRequest:
+                SendTagRequestResponse(uid, args, component.Tag);
+                break;
+            case NetCmdResponse when args.Data.TryGetValue(NetTag, out string? tag):
+                //Add the received tag request response to the list of targets
+                component.TargetList.Add(tag);
+                Dirty(uid, component);
+                break;
+        }
+    }
+
+    /// <summary>
+    /// Sends the given tag as a response to a <see cref="NetCmdRequest"/> if it's not null
+    /// </summary>
+    private void SendTagRequestResponse(EntityUid uid, DeviceNetworkPacketEvent args, string? tag)
+    {
+        if (tag == null)
+            return;
+
+        var payload = new NetworkPayload
+        {
+            [DeviceNetworkConstants.Command] = NetCmdResponse,
+            [NetTag] = tag
+        };
+
+        _deviceNetworkSystem.QueuePacket(uid, args.Address, payload, args.Frequency);
+    }
+
+    /// <summary>
+    /// Prevents the unit from flushing if no target is selected
+    /// </summary>
+    private void OnBeforeFlush(EntityUid uid, MailingUnitComponent component, BeforeDisposalFlushEvent args)
+    {
+        if (string.IsNullOrEmpty(component.Target))
+        {
+            args.Cancel();
+            return;
+        }
+
+        Dirty(uid, component);
+        args.Tags.Add(MailTag);
+        args.Tags.Add(component.Target);
+
+        BroadcastSentMessage(uid, component);
+    }
+
+    /// <summary>
+    /// Broadcast that a mail was sent including the src and target tags
+    /// </summary>
+    private void BroadcastSentMessage(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null)
+    {
+        if (string.IsNullOrEmpty(component.Tag) || string.IsNullOrEmpty(component.Target) || !Resolve(uid, ref device))
+            return;
+
+        var payload = new NetworkPayload
+        {
+            [DeviceNetworkConstants.Command] = NetCmdSent,
+            [NetSrc] = component.Tag,
+            [NetTarget] = component.Target
+        };
+
+        _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device);
+    }
+
+    /// <summary>
+    /// Clears the units target list and broadcasts a <see cref="NetCmdRequest"/>.
+    /// The target list will then get populated with <see cref="NetCmdResponse"/> responses from all active mailing units on the same grid
+    /// </summary>
+    private void UpdateTargetList(EntityUid uid, MailingUnitComponent component, DeviceNetworkComponent? device = null)
+    {
+        if (!Resolve(uid, ref device, false))
+            return;
+
+        var payload = new NetworkPayload
+        {
+            [DeviceNetworkConstants.Command] = NetCmdRequest
+        };
+
+        component.TargetList.Clear();
+        _deviceNetworkSystem.QueuePacket(uid, null, payload, null, null, device);
+    }
+
+    /// <summary>
+    /// Gets called when the units tag got updated
+    /// </summary>
+    private void OnConfigurationUpdated(EntityUid uid, MailingUnitComponent component, ConfigurationUpdatedEvent args)
+    {
+        var configuration = args.Configuration.Config;
+        if (!configuration.ContainsKey(TagConfigurationKey) || configuration[TagConfigurationKey] == string.Empty)
+        {
+            component.Tag = null;
+            return;
+        }
+
+        component.Tag = configuration[TagConfigurationKey];
+        Dirty(uid, component);
+    }
+
+    private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args)
+    {
+        if (args.Handled || !args.Complex)
+            return;
+
+        if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
+        {
+            return;
+        }
+
+        args.Handled = true;
+        UpdateTargetList(uid, component);
+        UserInterfaceSystem.OpenUi(uid, MailingUnitUiKey.Key, actor.PlayerSession);
+    }
+
+    private void OnTargetSelected(EntityUid uid, MailingUnitComponent component, TargetSelectedMessage args)
+    {
+        component.Target = args.Target;
+        Dirty(uid, component);
+    }
+}
diff --git a/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs b/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs
deleted file mode 100644 (file)
index 65be092..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-using Content.Shared.Disposal.Components;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Disposal;
-
-[Serializable, NetSerializable]
-public sealed class MailingUnitBoundUserInterfaceState : BoundUserInterfaceState, IEquatable<MailingUnitBoundUserInterfaceState>
-{
-    public string? Target;
-    public List<string> TargetList;
-    public string? Tag;
-    public SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState DisposalState;
-
-    public MailingUnitBoundUserInterfaceState(SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState disposalState, string? target, List<string> targetList, string? tag)
-    {
-        DisposalState = disposalState;
-        Target = target;
-        TargetList = targetList;
-        Tag = tag;
-    }
-
-    public bool Equals(MailingUnitBoundUserInterfaceState? other)
-    {
-        if (other is null)
-            return false;
-        if (ReferenceEquals(this, other))
-            return true;
-        return DisposalState.Equals(other.DisposalState)
-               && Target == other.Target
-               && TargetList.Equals(other.TargetList)
-               && Tag == other.Tag;
-    }
-
-    public override bool Equals(object? other)
-    {
-        if (other is MailingUnitBoundUserInterfaceState otherState)
-            return Equals(otherState);
-        return false;
-    }
-
-    public override int GetHashCode()
-    {
-        return base.GetHashCode();
-    }
-}
diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs
deleted file mode 100644 (file)
index a650ef7..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using Content.Shared.Body.Components;
-using Content.Shared.Disposal.Components;
-using Content.Shared.DoAfter;
-using Content.Shared.DragDrop;
-using Content.Shared.Emag.Systems;
-using Content.Shared.Item;
-using Content.Shared.Throwing;
-using Content.Shared.Whitelist;
-using Robust.Shared.Audio;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Physics.Events;
-using Robust.Shared.Physics.Systems;
-using Robust.Shared.Serialization;
-using Robust.Shared.Timing;
-
-namespace Content.Shared.Disposal;
-
-[Serializable, NetSerializable]
-public sealed partial class DisposalDoAfterEvent : SimpleDoAfterEvent
-{
-}
-
-public abstract class SharedDisposalUnitSystem : EntitySystem
-{
-    [Dependency] protected readonly IGameTiming GameTiming = default!;
-    [Dependency] protected readonly EmagSystem _emag = default!;
-    [Dependency] protected readonly MetaDataSystem Metadata = default!;
-    [Dependency] protected readonly SharedJointSystem Joints = default!;
-    [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
-
-    protected static TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5);
-
-    // Percentage
-    public const float PressurePerSecond = 0.05f;
-
-    public abstract bool HasDisposals([NotNullWhen(true)] EntityUid? uid);
-
-    public abstract bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component);
-
-    /// <summary>
-    /// Gets the current pressure state of a disposals unit.
-    /// </summary>
-    /// <param name="uid"></param>
-    /// <param name="component"></param>
-    /// <param name="metadata"></param>
-    /// <returns></returns>
-    public DisposalsPressureState GetState(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null)
-    {
-        var nextPressure = Metadata.GetPauseTime(uid, metadata) + component.NextPressurized - GameTiming.CurTime;
-        var pressurizeTime = 1f / PressurePerSecond;
-        var pressurizeDuration = pressurizeTime - component.FlushDelay.TotalSeconds;
-
-        if (nextPressure.TotalSeconds > pressurizeDuration)
-        {
-            return DisposalsPressureState.Flushed;
-        }
-
-        if (nextPressure > TimeSpan.Zero)
-        {
-            return DisposalsPressureState.Pressurizing;
-        }
-
-        return DisposalsPressureState.Ready;
-    }
-
-    public float GetPressure(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null)
-    {
-        if (!Resolve(uid, ref metadata))
-            return 0f;
-
-        var pauseTime = Metadata.GetPauseTime(uid, metadata);
-        return MathF.Min(1f,
-            (float) (GameTiming.CurTime - pauseTime - component.NextPressurized).TotalSeconds / PressurePerSecond);
-    }
-
-    protected void OnPreventCollide(EntityUid uid, SharedDisposalUnitComponent component,
-        ref PreventCollideEvent args)
-    {
-        var otherBody = args.OtherEntity;
-
-        // Items dropped shouldn't collide but items thrown should
-        if (HasComp<ItemComponent>(otherBody) && !HasComp<ThrownItemComponent>(otherBody))
-        {
-            args.Cancelled = true;
-            return;
-        }
-
-        if (component.RecentlyEjected.Contains(otherBody))
-        {
-            args.Cancelled = true;
-        }
-    }
-
-    protected void OnCanDragDropOn(EntityUid uid, SharedDisposalUnitComponent component, ref CanDropTargetEvent args)
-    {
-        if (args.Handled)
-            return;
-
-        args.CanDrop = CanInsert(uid, component, args.Dragged);
-        args.Handled = true;
-    }
-
-    protected void OnEmagged(EntityUid uid, SharedDisposalUnitComponent component, ref GotEmaggedEvent args)
-    {
-        if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
-            return;
-
-        if (component.DisablePressure == true)
-            return;
-
-        component.DisablePressure = true;
-        args.Handled = true;
-    }
-
-    public virtual bool CanInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid entity)
-    {
-        if (!Transform(uid).Anchored)
-            return false;
-
-        var storable = HasComp<ItemComponent>(entity);
-        if (!storable && !HasComp<BodyComponent>(entity))
-            return false;
-
-        if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) ||
-            _whitelistSystem.IsWhitelistFail(component.Whitelist, entity))
-            return false;
-
-        if (TryComp<PhysicsComponent>(entity, out var physics) && (physics.CanCollide) || storable)
-            return true;
-        else
-            return false;
-
-    }
-
-    public abstract void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null);
-
-    [Serializable, NetSerializable]
-    protected sealed class DisposalUnitComponentState : ComponentState
-    {
-        public SoundSpecifier? FlushSound;
-        public DisposalsPressureState State;
-        public TimeSpan NextPressurized;
-        public TimeSpan AutomaticEngageTime;
-        public TimeSpan? NextFlush;
-        public bool Powered;
-        public bool Engaged;
-        public List<NetEntity> RecentlyEjected;
-
-        public DisposalUnitComponentState(SoundSpecifier? flushSound, DisposalsPressureState state, TimeSpan nextPressurized, TimeSpan automaticEngageTime, TimeSpan? nextFlush, bool powered, bool engaged, List<NetEntity> recentlyEjected)
-        {
-            FlushSound = flushSound;
-            State = state;
-            NextPressurized = nextPressurized;
-            AutomaticEngageTime = automaticEngageTime;
-            NextFlush = nextFlush;
-            Powered = powered;
-            Engaged = engaged;
-            RecentlyEjected = recentlyEjected;
-        }
-    }
-}
diff --git a/Content.Shared/Disposal/Tube/DisposalEntryComponent.cs b/Content.Shared/Disposal/Tube/DisposalEntryComponent.cs
new file mode 100644 (file)
index 0000000..066b16a
--- /dev/null
@@ -0,0 +1,12 @@
+using Content.Shared.Disposal.Unit;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Disposal.Tube;
+
+[RegisterComponent]
+[Access(typeof(SharedDisposalTubeSystem), typeof(SharedDisposalUnitSystem))]
+public sealed partial class DisposalEntryComponent : Component
+{
+    [DataField]
+    public EntProtoId HolderPrototypeId = "DisposalHolder";
+}
diff --git a/Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs b/Content.Shared/Disposal/Unit/BeforeDisposalFlushEvent.cs
new file mode 100644 (file)
index 0000000..ee141d6
--- /dev/null
@@ -0,0 +1,10 @@
+namespace Content.Shared.Disposal.Unit.Events;
+
+/// <summary>
+/// Sent before the disposal unit flushes it's contents.
+/// Allows adding tags for sorting and preventing the disposal unit from flushing.
+/// </summary>
+public sealed class BeforeDisposalFlushEvent : CancellableEntityEventArgs
+{
+    public readonly List<string> Tags = new();
+}
\ No newline at end of file
similarity index 68%
rename from Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs
rename to Content.Shared/Disposal/Unit/DisposalUnitComponent.cs
index 36dd14f9b22b652ff1f723b5c34748cfb46aec95..34fe22301337034049262cd5c626781978254ac9 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Shared.Atmos;
 using Robust.Shared.Audio;
 using Content.Shared.Whitelist;
 using Robust.Shared.Containers;
@@ -7,15 +8,24 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 
 namespace Content.Shared.Disposal.Components;
 
-[NetworkedComponent]
-public abstract partial class SharedDisposalUnitComponent : Component
+/// <summary>
+/// Takes in entities and flushes them out to attached disposals tubes after a timer.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
+public sealed partial class DisposalUnitComponent : Component
 {
     public const string ContainerId = "disposals";
 
+    /// <summary>
+    /// Air contained in the disposal unit.
+    /// </summary>
+    [DataField]
+    public GasMixture Air = new(Atmospherics.CellVolume);
+
     /// <summary>
     /// Sounds played upon the unit flushing.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite), DataField("soundFlush")]
+    [DataField("soundFlush"), AutoNetworkedField]
     public SoundSpecifier? FlushSound = new SoundPathSpecifier("/Audio/Machines/disposalflush.ogg");
 
     /// <summary>
@@ -39,20 +49,13 @@ public abstract partial class SharedDisposalUnitComponent : Component
     /// <summary>
     /// State for this disposals unit.
     /// </summary>
-    [DataField]
+    [DataField, AutoNetworkedField]
     public DisposalsPressureState State;
 
-    // TODO: Just make this use vaulting.
-    /// <summary>
-    /// We'll track whatever just left disposals so we know what collision we need to ignore until they stop intersecting our BB.
-    /// </summary>
-    [ViewVariables, DataField]
-    public List<EntityUid> RecentlyEjected = new();
-
     /// <summary>
     /// Next time the disposal unit will be pressurized.
     /// </summary>
-    [DataField(customTypeSerializer:typeof(TimeOffsetSerializer))]
+    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
     public TimeSpan NextPressurized = TimeSpan.Zero;
 
     /// <summary>
@@ -70,26 +73,24 @@ public abstract partial class SharedDisposalUnitComponent : Component
     /// <summary>
     /// Removes the pressure requirement for flushing.
     /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite)]
+    [DataField]
     public bool DisablePressure;
 
     /// <summary>
     /// Last time that an entity tried to exit this disposal unit.
     /// </summary>
-    [ViewVariables]
+    [DataField, AutoNetworkedField]
     public TimeSpan LastExitAttempt;
 
     [DataField]
     public bool AutomaticEngage = true;
 
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField]
+    [DataField, AutoNetworkedField]
     public TimeSpan AutomaticEngageTime = TimeSpan.FromSeconds(30);
 
     /// <summary>
     /// Delay from trying to enter disposals ourselves.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
     [DataField]
     public float EntryDelay = 0.5f;
 
@@ -104,20 +105,16 @@ public abstract partial class SharedDisposalUnitComponent : Component
     /// </summary>
     [ViewVariables] public Container Container = default!;
 
-    // TODO: Network power shit instead fam.
-    [ViewVariables, DataField]
-    public bool Powered;
-
     /// <summary>
     /// Was the disposals unit engaged for a manual flush.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite), DataField]
+    [DataField, AutoNetworkedField]
     public bool Engaged;
 
     /// <summary>
     /// Next time this unit will flush. Is the lesser of <see cref="FlushDelay"/> and <see cref="AutomaticEngageTime"/>
     /// </summary>
-    [ViewVariables, DataField(customTypeSerializer:typeof(TimeOffsetSerializer))]
+    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
     public TimeSpan? NextFlush;
 
     [Serializable, NetSerializable]
@@ -162,37 +159,6 @@ public abstract partial class SharedDisposalUnitComponent : Component
         Power
     }
 
-    [Serializable, NetSerializable]
-    public sealed class DisposalUnitBoundUserInterfaceState : BoundUserInterfaceState, IEquatable<DisposalUnitBoundUserInterfaceState>
-    {
-        public readonly string UnitName;
-        public readonly string UnitState;
-        public readonly TimeSpan FullPressureTime;
-        public readonly bool Powered;
-        public readonly bool Engaged;
-
-        public DisposalUnitBoundUserInterfaceState(string unitName, string unitState, TimeSpan fullPressureTime, bool powered,
-            bool engaged)
-        {
-            UnitName = unitName;
-            UnitState = unitState;
-            FullPressureTime = fullPressureTime;
-            Powered = powered;
-            Engaged = engaged;
-        }
-
-        public bool Equals(DisposalUnitBoundUserInterfaceState? other)
-        {
-            if (ReferenceEquals(null, other)) return false;
-            if (ReferenceEquals(this, other)) return true;
-            return UnitName == other.UnitName &&
-                   UnitState == other.UnitState &&
-                   Powered == other.Powered &&
-                   Engaged == other.Engaged &&
-                   FullPressureTime.Equals(other.FullPressureTime);
-        }
-    }
-
     /// <summary>
     ///     Message data sent from client to server when a disposal unit ui button is pressed.
     /// </summary>
diff --git a/Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs b/Content.Shared/Disposal/Unit/SharedDisposalTubeSystem.cs
new file mode 100644 (file)
index 0000000..58d4da7
--- /dev/null
@@ -0,0 +1,14 @@
+using Content.Shared.Disposal.Components;
+
+namespace Content.Shared.Disposal.Unit;
+
+public abstract class SharedDisposalTubeSystem : EntitySystem
+{
+    public virtual bool TryInsert(EntityUid uid,
+        DisposalUnitComponent from,
+        IEnumerable<string>? tags = default,
+        Tube.DisposalEntryComponent? entry = null)
+    {
+        return false;
+    }
+}
similarity index 53%
rename from Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs
rename to Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs
index 136ac2c440f0bb562fddbd7b84be96e02176f27f..1db24c700def844343d6bb7a0b7c237b4cfdafaa 100644 (file)
@@ -1,24 +1,15 @@
-using System.Diagnostics.CodeAnalysis;
 using System.Linq;
-using Content.Server.Administration.Logs;
-using Content.Server.Atmos.EntitySystems;
-using Content.Server.Containers;
-using Content.Server.Disposal.Tube;
-using Content.Server.Disposal.Tube.Components;
-using Content.Server.Disposal.Unit.Components;
-using Content.Server.Popups;
-using Content.Server.Power.Components;
-using Content.Server.Power.EntitySystems;
 using Content.Shared.ActionBlocker;
-using Content.Shared.Atmos;
+using Content.Shared.Administration.Logs;
+using Content.Shared.Body.Components;
+using Content.Shared.Climbing.Systems;
+using Content.Shared.Containers;
 using Content.Shared.Database;
-using Content.Shared.Destructible;
-using Content.Shared.Disposal;
 using Content.Shared.Disposal.Components;
+using Content.Shared.Disposal.Unit.Events;
 using Content.Shared.DoAfter;
 using Content.Shared.DragDrop;
 using Content.Shared.Emag.Systems;
-using Content.Shared.Explosion;
 using Content.Shared.Hands.Components;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.IdentityManagement;
@@ -27,59 +18,59 @@ using Content.Shared.Item;
 using Content.Shared.Movement.Events;
 using Content.Shared.Popups;
 using Content.Shared.Power;
+using Content.Shared.Power.EntitySystems;
+using Content.Shared.Throwing;
 using Content.Shared.Verbs;
-using Robust.Server.Audio;
-using Robust.Server.GameObjects;
+using Content.Shared.Whitelist;
+using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
-using Robust.Shared.GameStates;
 using Robust.Shared.Map.Components;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Physics.Events;
-using Robust.Shared.Player;
+using Robust.Shared.Physics.Systems;
+using Robust.Shared.Serialization;
+using Robust.Shared.Timing;
 using Robust.Shared.Utility;
 
-namespace Content.Server.Disposal.Unit.EntitySystems;
+namespace Content.Shared.Disposal.Unit;
 
-public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
+[Serializable, NetSerializable]
+public sealed partial class DisposalDoAfterEvent : SimpleDoAfterEvent
 {
-    [Dependency] private readonly IAdminLogManager _adminLogger = default!;
-    [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
-    [Dependency] private readonly AppearanceSystem _appearance = default!;
-    [Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
-    [Dependency] private readonly AudioSystem _audioSystem = default!;
-    [Dependency] private readonly DisposalTubeSystem _disposalTubeSystem = default!;
-    [Dependency] private readonly EntityLookupSystem _lookup = default!;
-    [Dependency] private readonly PopupSystem _popupSystem = default!;
-    [Dependency] private readonly PowerReceiverSystem _power = default!;
-    [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
-    [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
-    [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
-    [Dependency] private readonly TransformSystem _transformSystem = default!;
-    [Dependency] private readonly UserInterfaceSystem _ui = default!;
-    [Dependency] private readonly SharedMapSystem _map = default!;
+}
+
+public abstract class SharedDisposalUnitSystem : EntitySystem
+{
+    [Dependency] protected readonly ActionBlockerSystem ActionBlockerSystem = default!;
+    [Dependency] private   readonly EntityWhitelistSystem _whitelistSystem = default!;
+    [Dependency] protected readonly MetaDataSystem Metadata = default!;
+    [Dependency] private   readonly SharedAppearanceSystem _appearance = default!;
+    [Dependency] protected readonly SharedAudioSystem Audio = default!;
+    [Dependency] protected readonly IGameTiming GameTiming = default!;
+    [Dependency] private   readonly ISharedAdminLogManager _adminLog = default!;
+    [Dependency] private   readonly ClimbSystem _climb = default!;
+    [Dependency] protected readonly SharedContainerSystem Containers = default!;
+    [Dependency] protected readonly SharedJointSystem Joints = default!;
+    [Dependency] private   readonly SharedPowerReceiverSystem _power = default!;
+    [Dependency] private   readonly SharedDisposalTubeSystem _disposalTubeSystem = default!;
+    [Dependency] private   readonly SharedPopupSystem _popupSystem = default!;
+    [Dependency] private   readonly SharedDoAfterSystem _doAfterSystem = default!;
+    [Dependency] private   readonly SharedHandsSystem _handsSystem = default!;
+    [Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
+    [Dependency] private   readonly SharedUserInterfaceSystem _ui = default!;
+    [Dependency] private   readonly SharedMapSystem _map = default!;
+
+    protected static TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5);
+
+    // Percentage
+    public const float PressurePerSecond = 0.05f;
 
     public override void Initialize()
     {
         base.Initialize();
 
-        SubscribeLocalEvent<DisposalUnitComponent, ComponentGetState>(OnGetState);
         SubscribeLocalEvent<DisposalUnitComponent, PreventCollideEvent>(OnPreventCollide);
         SubscribeLocalEvent<DisposalUnitComponent, CanDropTargetEvent>(OnCanDragDropOn);
-        SubscribeLocalEvent<DisposalUnitComponent, GotEmaggedEvent>(OnEmagged);
-
-        // Shouldn't need re-anchoring.
-        SubscribeLocalEvent<DisposalUnitComponent, AnchorStateChangedEvent>(OnAnchorChanged);
-        // TODO: Predict me when hands predicted
-        SubscribeLocalEvent<DisposalUnitComponent, ContainerRelayMovementEntityEvent>(OnMovement);
-        SubscribeLocalEvent<DisposalUnitComponent, PowerChangedEvent>(OnPowerChange);
-        SubscribeLocalEvent<DisposalUnitComponent, ComponentInit>(OnDisposalInit);
-
-        SubscribeLocalEvent<DisposalUnitComponent, ActivateInWorldEvent>(OnActivate);
-        SubscribeLocalEvent<DisposalUnitComponent, AfterInteractUsingEvent>(OnAfterInteractUsing);
-        SubscribeLocalEvent<DisposalUnitComponent, DragDropTargetEvent>(OnDragDropOn);
-        SubscribeLocalEvent<DisposalUnitComponent, DestructionEventArgs>(OnDestruction);
-        SubscribeLocalEvent<DisposalUnitComponent, BeforeExplodeEvent>(OnExploded);
-
         SubscribeLocalEvent<DisposalUnitComponent, GetVerbsEvent<InteractionVerb>>(AddInsertVerb);
         SubscribeLocalEvent<DisposalUnitComponent, GetVerbsEvent<AlternativeVerb>>(AddDisposalAltVerbs);
         SubscribeLocalEvent<DisposalUnitComponent, GetVerbsEvent<Verb>>(AddClimbInsideVerb);
@@ -88,27 +79,27 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
 
         SubscribeLocalEvent<DisposalUnitComponent, BeforeThrowInsertEvent>(OnThrowInsert);
 
-        SubscribeLocalEvent<DisposalUnitComponent, SharedDisposalUnitComponent.UiButtonPressedMessage>(OnUiButtonPressed);
-    }
+        SubscribeLocalEvent<DisposalUnitComponent, DisposalUnitComponent.UiButtonPressedMessage>(OnUiButtonPressed);
 
-    private void OnGetState(EntityUid uid, DisposalUnitComponent component, ref ComponentGetState args)
-    {
-        args.State = new DisposalUnitComponentState(
-            component.FlushSound,
-            component.State,
-            component.NextPressurized,
-            component.AutomaticEngageTime,
-            component.NextFlush,
-            component.Powered,
-            component.Engaged,
-            GetNetEntityList(component.RecentlyEjected));
+        SubscribeLocalEvent<DisposalUnitComponent, GotEmaggedEvent>(OnEmagged);
+        SubscribeLocalEvent<DisposalUnitComponent, AnchorStateChangedEvent>(OnAnchorChanged);
+        SubscribeLocalEvent<DisposalUnitComponent, PowerChangedEvent>(OnPowerChange);
+        SubscribeLocalEvent<DisposalUnitComponent, ComponentInit>(OnDisposalInit);
+
+        SubscribeLocalEvent<DisposalUnitComponent, ActivateInWorldEvent>(OnActivate);
+        SubscribeLocalEvent<DisposalUnitComponent, AfterInteractUsingEvent>(OnAfterInteractUsing);
+        SubscribeLocalEvent<DisposalUnitComponent, DragDropTargetEvent>(OnDragDropOn);
+        SubscribeLocalEvent<DisposalUnitComponent, ContainerRelayMovementEntityEvent>(OnMovement);
     }
 
-    private void AddDisposalAltVerbs(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent<AlternativeVerb> args)
+    private void AddDisposalAltVerbs(Entity<DisposalUnitComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
     {
         if (!args.CanAccess || !args.CanInteract)
             return;
 
+        var uid = ent.Owner;
+        var component = ent.Comp;
+
         // Behavior for if the disposals bin has items in it
         if (component.Container.ContainedEntities.Count > 0)
         {
@@ -133,41 +124,12 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
         }
     }
 
-    private void AddClimbInsideVerb(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent<Verb> args)
-    {
-        // This is not an interaction, activation, or alternative verb type because unfortunately most users are
-        // unwilling to accept that this is where they belong and don't want to accidentally climb inside.
-        if (!args.CanAccess ||
-            !args.CanInteract ||
-            component.Container.ContainedEntities.Contains(args.User) ||
-            !_actionBlockerSystem.CanMove(args.User))
-        {
-            return;
-        }
-
-        if (!CanInsert(uid, component, args.User))
-            return;
-
-        // Add verb to climb inside of the unit,
-        Verb verb = new()
-        {
-            Act = () => TryInsert(uid, args.User, args.User),
-            DoContactInteraction = true,
-            Text = Loc.GetString("disposal-self-insert-verb-get-data-text")
-        };
-        // TODO VERB ICON
-        // TODO VERB CATEGORY
-        // create a verb category for "enter"?
-        // See also, medical scanner. Also maybe add verbs for entering lockers/body bags?
-        args.Verbs.Add(verb);
-    }
-
-    private void AddInsertVerb(EntityUid uid, SharedDisposalUnitComponent component, GetVerbsEvent<InteractionVerb> args)
+    private void AddInsertVerb(EntityUid uid, DisposalUnitComponent component, GetVerbsEvent<InteractionVerb> args)
     {
         if (!args.CanAccess || !args.CanInteract || args.Hands == null || args.Using == null)
             return;
 
-        if (!_actionBlockerSystem.CanDrop(args.User))
+        if (!ActionBlockerSystem.CanDrop(args.User))
             return;
 
         if (!CanInsert(uid, component, args.Using.Value))
@@ -180,7 +142,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
             Act = () =>
             {
                 _handsSystem.TryDropIntoContainer(args.User, args.Using.Value, component.Container, checkActionBlocker: false, args.Hands);
-                _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}");
+                _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}");
                 AfterInsert(uid, component, args.Using.Value, args.User);
             }
         };
@@ -188,7 +150,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
         args.Verbs.Add(insertVerb);
     }
 
-    private void OnDoAfter(EntityUid uid, SharedDisposalUnitComponent component, DoAfterEvent args)
+    private void OnDoAfter(EntityUid uid, DisposalUnitComponent component, DoAfterEvent args)
     {
         if (args.Handled || args.Cancelled || args.Args.Target == null || args.Args.Used == null)
             return;
@@ -204,89 +166,46 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
             args.Cancelled = true;
     }
 
-    public override void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null)
-    {
-        if (!ResolveDisposals(uid, ref disposal))
-            return;
-
-        if (!_containerSystem.Insert(toInsert, disposal.Container))
-            return;
-
-        _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(uid)}");
-        AfterInsert(uid, disposal, toInsert, user);
-    }
-
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
 
-        var query = AllEntityQuery<DisposalUnitComponent, MetaDataComponent>();
+        var query = EntityQueryEnumerator<DisposalUnitComponent, MetaDataComponent>();
         while (query.MoveNext(out var uid, out var unit, out var metadata))
         {
-            if (!metadata.EntityPaused)
-                Update(uid, unit, metadata, frameTime);
+            Update(uid, unit, metadata);
         }
     }
 
-    #region UI Handlers
-    private void OnUiButtonPressed(EntityUid uid, SharedDisposalUnitComponent component, SharedDisposalUnitComponent.UiButtonPressedMessage args)
+    // TODO: This should just use the same thing as entity storage?
+    private void OnMovement(EntityUid uid, DisposalUnitComponent component, ref ContainerRelayMovementEntityEvent args)
     {
-        if (args.Actor is not { Valid: true } player)
-        {
-            return;
-        }
+        var currentTime = GameTiming.CurTime;
 
-        switch (args.Button)
-        {
-            case SharedDisposalUnitComponent.UiButton.Eject:
-                TryEjectContents(uid, component);
-                _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit eject button on {ToPrettyString(uid)}");
-                break;
-            case SharedDisposalUnitComponent.UiButton.Engage:
-                ToggleEngage(uid, component);
-                _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit flush button on {ToPrettyString(uid)}, it's now {(component.Engaged ? "on" : "off")}");
-                break;
-            case SharedDisposalUnitComponent.UiButton.Power:
-                _power.TogglePower(uid, user: args.Actor);
-                break;
-            default:
-                throw new ArgumentOutOfRangeException($"{ToPrettyString(player):player} attempted to hit a nonexistant button on {ToPrettyString(uid)}");
-        }
-    }
+        if (!ActionBlockerSystem.CanMove(args.Entity))
+            return;
 
-    public void ToggleEngage(EntityUid uid, SharedDisposalUnitComponent component)
-    {
-        component.Engaged ^= true;
+        if (!TryComp(args.Entity, out HandsComponent? hands) ||
+            hands.Count == 0 ||
+            currentTime < component.LastExitAttempt + ExitAttemptDelay)
+            return;
 
-        if (component.Engaged)
-        {
-            ManualEngage(uid, component);
-        }
-        else
-        {
-            Disengage(uid, component);
-        }
+        Dirty(uid, component);
+        component.LastExitAttempt = currentTime;
+        Remove(uid, component, args.Entity);
+        UpdateUI((uid, component));
     }
 
-    #endregion
-
-    #region Eventbus Handlers
-
-    private void OnActivate(EntityUid uid, SharedDisposalUnitComponent component, ActivateInWorldEvent args)
+    private void OnActivate(EntityUid uid, DisposalUnitComponent component, ActivateInWorldEvent args)
     {
         if (args.Handled || !args.Complex)
             return;
 
-        if (!TryComp(args.User, out ActorComponent? actor))
-        {
-            return;
-        }
-
         args.Handled = true;
-        _ui.OpenUi(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, actor.PlayerSession);
+        _ui.TryToggleUi(uid, DisposalUnitComponent.DisposalUnitUiKey.Key, args.User);
     }
 
-    private void OnAfterInteractUsing(EntityUid uid, SharedDisposalUnitComponent component, AfterInteractUsingEvent args)
+    private void OnAfterInteractUsing(EntityUid uid, DisposalUnitComponent component, AfterInteractUsingEvent args)
     {
         if (args.Handled || !args.CanReach)
             return;
@@ -301,26 +220,23 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
             return;
         }
 
-        _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Used)} into {ToPrettyString(uid)}");
+        _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Used)} into {ToPrettyString(uid)}");
         AfterInsert(uid, component, args.Used, args.User);
         args.Handled = true;
     }
 
-    private void OnDisposalInit(EntityUid uid, SharedDisposalUnitComponent component, ComponentInit args)
+    protected virtual void OnDisposalInit(Entity<DisposalUnitComponent> ent, ref ComponentInit args)
     {
-        component.Container = _containerSystem.EnsureContainer<Container>(uid, SharedDisposalUnitComponent.ContainerId);
-
-        UpdateInterface(uid, component, component.Powered);
+        ent.Comp.Container = Containers.EnsureContainer<Container>(ent, DisposalUnitComponent.ContainerId);
     }
 
-    private void OnPowerChange(EntityUid uid, SharedDisposalUnitComponent component, ref PowerChangedEvent args)
+    private void OnPowerChange(EntityUid uid, DisposalUnitComponent component, ref PowerChangedEvent args)
     {
-        if (!component.Running || args.Powered == component.Powered)
+        if (!component.Running)
             return;
 
-        component.Powered = args.Powered;
+        UpdateUI((uid, component));
         UpdateVisualState(uid, component);
-        UpdateInterface(uid, component, args.Powered);
 
         if (!args.Powered)
         {
@@ -336,24 +252,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
         }
     }
 
-    // TODO: This should just use the same thing as entity storage?
-    private void OnMovement(EntityUid uid, SharedDisposalUnitComponent component, ref ContainerRelayMovementEntityEvent args)
-    {
-        var currentTime = GameTiming.CurTime;
-
-        if (!_actionBlockerSystem.CanMove(args.Entity))
-            return;
-
-        if (!TryComp(args.Entity, out HandsComponent? hands) ||
-            hands.Count == 0 ||
-            currentTime < component.LastExitAttempt + ExitAttemptDelay)
-            return;
-
-        component.LastExitAttempt = currentTime;
-        Remove(uid, component, args.Entity);
-    }
-
-    private void OnAnchorChanged(EntityUid uid, SharedDisposalUnitComponent component, ref AnchorStateChangedEvent args)
+    private void OnAnchorChanged(EntityUid uid, DisposalUnitComponent component, ref AnchorStateChangedEvent args)
     {
         if (Terminating(uid))
             return;
@@ -363,108 +262,238 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
             TryEjectContents(uid, component);
     }
 
-    private void OnDestruction(EntityUid uid, SharedDisposalUnitComponent component, DestructionEventArgs args)
+    private void OnDragDropOn(EntityUid uid, DisposalUnitComponent component, ref DragDropTargetEvent args)
     {
-        TryEjectContents(uid, component);
+        args.Handled = TryInsert(uid, args.Dragged, args.User);
     }
 
-    private void OnDragDropOn(EntityUid uid, SharedDisposalUnitComponent component, ref DragDropTargetEvent args)
+    protected virtual void UpdateUI(Entity<DisposalUnitComponent> entity)
     {
-        args.Handled = TryInsert(uid, args.Dragged, args.User);
+
     }
 
-    #endregion
+    /// <summary>
+    /// Returns the estimated time when the disposal unit will be back to full pressure.
+    /// </summary>
+    public TimeSpan EstimatedFullPressure(EntityUid uid, DisposalUnitComponent component)
+    {
+        if (component.NextPressurized < GameTiming.CurTime)
+            return TimeSpan.Zero;
 
-    private void UpdateState(EntityUid uid, DisposalsPressureState state, SharedDisposalUnitComponent component, MetaDataComponent metadata)
+        return component.NextPressurized;
+    }
+
+    public bool CanFlush(EntityUid unit, DisposalUnitComponent component)
     {
-        if (component.State == state)
+        return GetState(unit, component) == DisposalsPressureState.Ready
+               && _power.IsPowered(unit)
+               && Comp<TransformComponent>(unit).Anchored;
+    }
+
+    public void Remove(EntityUid uid, DisposalUnitComponent component, EntityUid toRemove)
+    {
+        if (GameTiming.ApplyingState)
             return;
 
-        component.State = state;
-        UpdateVisualState(uid, component);
-        UpdateInterface(uid, component, component.Powered);
-        Dirty(uid, component, metadata);
+        if (!Containers.Remove(toRemove, component.Container))
+            return;
 
-        if (state == DisposalsPressureState.Ready)
+        if (component.Container.ContainedEntities.Count == 0)
         {
-            component.NextPressurized = TimeSpan.Zero;
-
-            // Manually engaged
-            if (component.Engaged)
-            {
-                component.NextFlush = GameTiming.CurTime + component.ManualFlushTime;
-            }
-            else if (component.Container.ContainedEntities.Count > 0)
-            {
-                component.NextFlush = GameTiming.CurTime + component.AutomaticEngageTime;
-            }
-            else
+            // If not manually engaged then reset the flushing entirely.
+            if (!component.Engaged)
             {
                 component.NextFlush = null;
+                Dirty(uid, component);
+                UpdateUI((uid, component));
             }
         }
+
+        _climb.Climb(toRemove, toRemove, uid, silent: true);
+
+        UpdateVisualState(uid, component);
     }
 
-    /// <summary>
-    /// Work out if we can stop updating this disposals component i.e. full pressure and nothing colliding.
-    /// </summary>
-    private void Update(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent metadata, float frameTime)
+    public void UpdateVisualState(EntityUid uid, DisposalUnitComponent component, bool flush = false)
     {
-        var state = GetState(uid, component, metadata);
+        if (!TryComp(uid, out AppearanceComponent? appearance))
+        {
+            return;
+        }
 
-        // Pressurizing, just check if we need a state update.
-        if (component.NextPressurized > GameTiming.CurTime)
+        if (!Transform(uid).Anchored)
         {
-            UpdateState(uid, state, component, metadata);
+            _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.UnAnchored, appearance);
+            _appearance.SetData(uid, DisposalUnitComponent.Visuals.Handle, DisposalUnitComponent.HandleState.Normal, appearance);
+            _appearance.SetData(uid, DisposalUnitComponent.Visuals.Light, DisposalUnitComponent.LightStates.Off, appearance);
             return;
         }
 
-        if (component.NextFlush != null)
+        var state = GetState(uid, component);
+
+        switch (state)
         {
-            if (component.NextFlush.Value < GameTiming.CurTime)
-            {
-                TryFlush(uid, component);
-            }
+            case DisposalsPressureState.Flushed:
+                _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.OverlayFlushing, appearance);
+                break;
+            case DisposalsPressureState.Pressurizing:
+                _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.OverlayCharging, appearance);
+                break;
+            case DisposalsPressureState.Ready:
+                _appearance.SetData(uid, DisposalUnitComponent.Visuals.VisualState, DisposalUnitComponent.VisualState.Anchored, appearance);
+                break;
         }
 
-        UpdateState(uid, state, component, metadata);
+        _appearance.SetData(uid, DisposalUnitComponent.Visuals.Handle, component.Engaged
+            ? DisposalUnitComponent.HandleState.Engaged
+            : DisposalUnitComponent.HandleState.Normal, appearance);
 
-        Box2? disposalsBounds = null;
-        var count = component.RecentlyEjected.Count;
+        if (!_power.IsPowered(uid))
+        {
+            _appearance.SetData(uid, DisposalUnitComponent.Visuals.Light, DisposalUnitComponent.LightStates.Off, appearance);
+            return;
+        }
+
+        var lightState = DisposalUnitComponent.LightStates.Off;
 
-        if (count > 0)
+        if (component.Container.ContainedEntities.Count > 0)
         {
-            if (!HasComp<PhysicsComponent>(uid))
-            {
-                component.RecentlyEjected.Clear();
-            }
-            else
-            {
-                disposalsBounds = _lookup.GetWorldAABB(uid);
-            }
+            lightState |= DisposalUnitComponent.LightStates.Full;
         }
 
-        for (var i = 0; i < component.RecentlyEjected.Count; i++)
+        if (state is DisposalsPressureState.Pressurizing or DisposalsPressureState.Flushed)
         {
-            var ejectedId = component.RecentlyEjected[i];
-            if (HasComp<PhysicsComponent>(ejectedId))
-            {
-                // TODO: We need to use a specific collision method (which sloth hasn't coded yet) for actual bounds overlaps.
-                // TODO: Come do this sloth :^)
-                // Check for itemcomp as we won't just block the disposal unit "sleeping" for something it can't collide with anyway.
-                if (!HasComp<ItemComponent>(ejectedId)
-                    && _lookup.GetWorldAABB(ejectedId).Intersects(disposalsBounds!.Value))
-                {
-                    continue;
-                }
-
-                component.RecentlyEjected.RemoveAt(i);
-                i--;
-            }
+            lightState |= DisposalUnitComponent.LightStates.Charging;
+        }
+        else
+        {
+            lightState |= DisposalUnitComponent.LightStates.Ready;
+        }
+
+        _appearance.SetData(uid, DisposalUnitComponent.Visuals.Light, lightState, appearance);
+    }
+
+    /// <summary>
+    /// Gets the current pressure state of a disposals unit.
+    /// </summary>
+    /// <param name="uid"></param>
+    /// <param name="component"></param>
+    /// <param name="metadata"></param>
+    /// <returns></returns>
+    public DisposalsPressureState GetState(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null)
+    {
+        var nextPressure = Metadata.GetPauseTime(uid, metadata) + component.NextPressurized - GameTiming.CurTime;
+        var pressurizeTime = 1f / PressurePerSecond;
+        var pressurizeDuration = pressurizeTime - component.FlushDelay.TotalSeconds;
+
+        if (nextPressure.TotalSeconds > pressurizeDuration)
+        {
+            return DisposalsPressureState.Flushed;
+        }
+
+        if (nextPressure > TimeSpan.Zero)
+        {
+            return DisposalsPressureState.Pressurizing;
+        }
+
+        return DisposalsPressureState.Ready;
+    }
+
+    public float GetPressure(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null)
+    {
+        if (!Resolve(uid, ref metadata))
+            return 0f;
+
+        var pauseTime = Metadata.GetPauseTime(uid, metadata);
+        return MathF.Min(1f,
+            (float)(GameTiming.CurTime - pauseTime - component.NextPressurized).TotalSeconds / PressurePerSecond);
+    }
+
+    protected void OnPreventCollide(EntityUid uid, DisposalUnitComponent component,
+        ref PreventCollideEvent args)
+    {
+        var otherBody = args.OtherEntity;
+
+        // Items dropped shouldn't collide but items thrown should
+        if (HasComp<ItemComponent>(otherBody) && !HasComp<ThrownItemComponent>(otherBody))
+        {
+            args.Cancelled = true;
         }
+    }
 
-        if (count != component.RecentlyEjected.Count)
-            Dirty(uid, component, metadata);
+    protected void OnCanDragDropOn(EntityUid uid, DisposalUnitComponent component, ref CanDropTargetEvent args)
+    {
+        if (args.Handled)
+            return;
+
+        args.CanDrop = CanInsert(uid, component, args.Dragged);
+        args.Handled = true;
+    }
+
+    protected void OnEmagged(EntityUid uid, DisposalUnitComponent component, ref GotEmaggedEvent args)
+    {
+        component.DisablePressure = true;
+        args.Handled = true;
+    }
+
+    public virtual bool CanInsert(EntityUid uid, DisposalUnitComponent component, EntityUid entity)
+    {
+        // TODO: All of the below should be using the EXISTING EVENT
+        if (!Containers.CanInsert(entity, component.Container))
+            return false;
+
+        if (!Transform(uid).Anchored)
+            return false;
+
+        var storable = HasComp<ItemComponent>(entity);
+        if (!storable && !HasComp<BodyComponent>(entity))
+            return false;
+
+        if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) ||
+            _whitelistSystem.IsWhitelistFail(component.Whitelist, entity))
+            return false;
+
+        if (TryComp<PhysicsComponent>(entity, out var physics) && (physics.CanCollide) || storable)
+            return true;
+        else
+            return false;
+    }
+
+    public void DoInsertDisposalUnit(EntityUid uid,
+        EntityUid toInsert,
+        EntityUid user,
+        DisposalUnitComponent? disposal = null)
+    {
+        if (!Resolve(uid, ref disposal))
+            return;
+
+        if (!Containers.Insert(toInsert, disposal.Container))
+            return;
+
+        _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} inserted {ToPrettyString(toInsert)} into {ToPrettyString(uid)}");
+        AfterInsert(uid, disposal, toInsert, user);
+    }
+
+    public virtual void AfterInsert(EntityUid uid,
+        DisposalUnitComponent component,
+        EntityUid inserted,
+        EntityUid? user = null,
+        bool doInsert = false)
+    {
+        Audio.PlayPredicted(component.InsertSound, uid, user: user);
+        if (doInsert && !Containers.Insert(inserted, component.Container))
+            return;
+
+        if (user != inserted && user != null)
+            _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user.Value):player} inserted {ToPrettyString(inserted)} into {ToPrettyString(uid)}");
+
+        QueueAutomaticEngage(uid, component);
+
+        _ui.CloseUi(uid, DisposalUnitComponent.DisposalUnitUiKey.Key, inserted);
+
+        // Maybe do pullable instead? Eh still fine.
+        Joints.RecursiveClearJoints(inserted);
+        UpdateVisualState(uid, component);
     }
 
     public bool TryInsert(EntityUid unitId, EntityUid toInsertId, EntityUid? userId, DisposalUnitComponent? unit = null)
@@ -507,8 +536,61 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
         return true;
     }
 
+    private void UpdateState(EntityUid uid, DisposalsPressureState state, DisposalUnitComponent component, MetaDataComponent metadata)
+    {
+        if (component.State == state)
+            return;
+
+        component.State = state;
+        UpdateVisualState(uid, component);
+        Dirty(uid, component, metadata);
+
+        if (state == DisposalsPressureState.Ready)
+        {
+            component.NextPressurized = TimeSpan.Zero;
+
+            // Manually engaged
+            if (component.Engaged)
+            {
+                component.NextFlush = GameTiming.CurTime + component.ManualFlushTime;
+            }
+            else if (component.Container.ContainedEntities.Count > 0)
+            {
+                component.NextFlush = GameTiming.CurTime + component.AutomaticEngageTime;
+            }
+            else
+            {
+                component.NextFlush = null;
+            }
+        }
+    }
+
+    /// <summary>
+    /// Work out if we can stop updating this disposals component i.e. full pressure and nothing colliding.
+    /// </summary>
+    private void Update(EntityUid uid, DisposalUnitComponent component, MetaDataComponent metadata)
+    {
+        var state = GetState(uid, component, metadata);
+
+        // Pressurizing, just check if we need a state update.
+        if (component.NextPressurized > GameTiming.CurTime)
+        {
+            UpdateState(uid, state, component, metadata);
+            return;
+        }
+
+        if (component.NextFlush != null)
+        {
+            if (component.NextFlush.Value < GameTiming.CurTime)
+            {
+                TryFlush(uid, component);
+            }
+        }
+
+        UpdateState(uid, state, component, metadata);
+    }
 
-    public bool TryFlush(EntityUid uid, SharedDisposalUnitComponent component)
+    public bool TryFlush(EntityUid uid, DisposalUnitComponent component)
     {
         if (!CanFlush(uid, component))
         {
@@ -533,11 +615,12 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
 
         var coords = xform.Coordinates;
         var entry = _map.GetLocal(xform.GridUid.Value, grid, coords)
-            .FirstOrDefault(HasComp<DisposalEntryComponent>);
+            .FirstOrDefault(HasComp<Tube.DisposalEntryComponent>);
 
         if (entry == default || component is not DisposalUnitComponent sDisposals)
         {
             component.Engaged = false;
+            UpdateUI((uid, component));
             Dirty(uid, component);
             return false;
         }
@@ -555,140 +638,23 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
         component.NextFlush = null;
 
         UpdateVisualState(uid, component, true);
-        UpdateInterface(uid, component, component.Powered);
-
         Dirty(uid, component);
+        UpdateUI((uid, component));
 
         return true;
     }
 
-    private void HandleAir(EntityUid uid, DisposalUnitComponent component, TransformComponent xform)
-    {
-        var air = component.Air;
-        var indices = _transformSystem.GetGridTilePositionOrDefault((uid, xform));
-
-        if (_atmosSystem.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is { Temperature: > 0f } environment)
-        {
-            var transferMoles = 0.1f * (0.25f * Atmospherics.OneAtmosphere * 1.01f - air.Pressure) * air.Volume / (environment.Temperature * Atmospherics.R);
-
-            component.Air = environment.Remove(transferMoles);
-        }
-    }
-
-    public void UpdateInterface(EntityUid uid, SharedDisposalUnitComponent component, bool powered)
-    {
-        var compState = GetState(uid, component);
-        var stateString = Loc.GetString($"disposal-unit-state-{compState}");
-        var state = new SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState(Name(uid), stateString, EstimatedFullPressure(uid, component), powered, component.Engaged);
-        _ui.SetUiState(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, state);
-
-        var stateUpdatedEvent = new DisposalUnitUIStateUpdatedEvent(state);
-        RaiseLocalEvent(uid, stateUpdatedEvent);
-    }
-
-    /// <summary>
-    /// Returns the estimated time when the disposal unit will be back to full pressure.
-    /// </summary>
-    private TimeSpan EstimatedFullPressure(EntityUid uid, SharedDisposalUnitComponent component)
-    {
-        if (component.NextPressurized < GameTiming.CurTime)
-            return TimeSpan.Zero;
-
-        return component.NextPressurized;
-    }
-
-    public void UpdateVisualState(EntityUid uid, SharedDisposalUnitComponent component, bool flush = false)
-    {
-        if (!TryComp(uid, out AppearanceComponent? appearance))
-        {
-            return;
-        }
-
-        if (!Transform(uid).Anchored)
-        {
-            _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.UnAnchored, appearance);
-            _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Handle, SharedDisposalUnitComponent.HandleState.Normal, appearance);
-            _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightStates.Off, appearance);
-            return;
-        }
-
-        var state = GetState(uid, component);
-
-        switch (state)
-        {
-            case DisposalsPressureState.Flushed:
-                _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayFlushing, appearance);
-                break;
-            case DisposalsPressureState.Pressurizing:
-                _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayCharging, appearance);
-                break;
-            case DisposalsPressureState.Ready:
-                _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Anchored, appearance);
-                break;
-        }
-
-        _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Handle, component.Engaged
-            ? SharedDisposalUnitComponent.HandleState.Engaged
-            : SharedDisposalUnitComponent.HandleState.Normal, appearance);
-
-        if (!component.Powered)
-        {
-            _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, SharedDisposalUnitComponent.LightStates.Off, appearance);
-            return;
-        }
-
-        var lightState = SharedDisposalUnitComponent.LightStates.Off;
-
-        if (component.Container.ContainedEntities.Count > 0)
-        {
-            lightState |= SharedDisposalUnitComponent.LightStates.Full;
-        }
-
-        if (state is DisposalsPressureState.Pressurizing or DisposalsPressureState.Flushed)
-        {
-            lightState |= SharedDisposalUnitComponent.LightStates.Charging;
-        }
-        else
-        {
-            lightState |= SharedDisposalUnitComponent.LightStates.Ready;
-        }
-
-        _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.Light, lightState, appearance);
-    }
-
-    public void Remove(EntityUid uid, SharedDisposalUnitComponent component, EntityUid toRemove)
+    protected virtual void HandleAir(EntityUid uid, DisposalUnitComponent component, TransformComponent xform)
     {
-        _containerSystem.Remove(toRemove, component.Container);
-
-        if (component.Container.ContainedEntities.Count == 0)
-        {
-            // If not manually engaged then reset the flushing entirely.
-            if (!component.Engaged)
-            {
-                component.NextFlush = null;
-            }
-        }
-
-        if (!component.RecentlyEjected.Contains(toRemove))
-            component.RecentlyEjected.Add(toRemove);
 
-        UpdateVisualState(uid, component);
-        Dirty(uid, component);
-    }
-
-    public bool CanFlush(EntityUid unit, SharedDisposalUnitComponent component)
-    {
-        return GetState(unit, component) == DisposalsPressureState.Ready
-               && component.Powered
-               && Comp<TransformComponent>(unit).Anchored;
     }
 
-    public void ManualEngage(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null)
+    public void ManualEngage(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null)
     {
         component.Engaged = true;
         UpdateVisualState(uid, component);
-        UpdateInterface(uid, component, component.Powered);
         Dirty(uid, component);
+        UpdateUI((uid, component));
 
         if (!CanFlush(uid, component))
             return;
@@ -701,7 +667,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
         component.NextFlush = TimeSpan.FromSeconds(Math.Min((component.NextFlush ?? TimeSpan.MaxValue).TotalSeconds, nextEngage.TotalSeconds));
     }
 
-    public void Disengage(EntityUid uid, SharedDisposalUnitComponent component)
+    public void Disengage(EntityUid uid, DisposalUnitComponent component)
     {
         component.Engaged = false;
 
@@ -711,14 +677,14 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
         }
 
         UpdateVisualState(uid, component);
-        UpdateInterface(uid, component, component.Powered);
         Dirty(uid, component);
+        UpdateUI((uid, component));
     }
 
     /// <summary>
     /// Remove all entities currently in the disposal unit.
     /// </summary>
-    public void TryEjectContents(EntityUid uid, SharedDisposalUnitComponent component)
+    public void TryEjectContents(EntityUid uid, DisposalUnitComponent component)
     {
         foreach (var entity in component.Container.ContainedEntities.ToArray())
         {
@@ -729,38 +695,16 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
         {
             component.NextFlush = null;
             Dirty(uid, component);
+            UpdateUI((uid, component));
         }
     }
 
-    public override bool HasDisposals(EntityUid? uid)
-    {
-        return HasComp<DisposalUnitComponent>(uid);
-    }
-
-    public override bool ResolveDisposals(EntityUid uid, [NotNullWhen(true)] ref SharedDisposalUnitComponent? component)
-    {
-        if (component != null)
-            return true;
-
-        TryComp<DisposalUnitComponent>(uid, out var storage);
-        component = storage;
-        return component != null;
-    }
-
-    public override bool CanInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid entity)
-    {
-        if (!base.CanInsert(uid, component, entity))
-            return false;
-
-        return _containerSystem.CanInsert(entity, component.Container);
-    }
-
     /// <summary>
     /// If something is inserted (or the likes) then we'll queue up an automatic flush in the future.
     /// </summary>
-    public void QueueAutomaticEngage(EntityUid uid, SharedDisposalUnitComponent component, MetaDataComponent? metadata = null)
+    public void QueueAutomaticEngage(EntityUid uid, DisposalUnitComponent component, MetaDataComponent? metadata = null)
     {
-        if (component.Deleted || !component.AutomaticEngage || !component.Powered && component.Container.ContainedEntities.Count == 0)
+        if (component.Deleted || !component.AutomaticEngage || !_power.IsPowered(uid) && component.Container.ContainedEntities.Count == 0)
         {
             return;
         }
@@ -771,53 +715,74 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem
 
         component.NextFlush = flushTime;
         Dirty(uid, component);
+        UpdateUI((uid, component));
     }
 
-    public void AfterInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid inserted, EntityUid? user = null, bool doInsert = false)
+    private void OnUiButtonPressed(EntityUid uid, DisposalUnitComponent component, DisposalUnitComponent.UiButtonPressedMessage args)
     {
-        _audioSystem.PlayPvs(component.InsertSound, uid);
-
-        if (doInsert && !_containerSystem.Insert(inserted, component.Container))
+        if (args.Actor is not { Valid: true } player)
+        {
             return;
+        }
 
-        if (user != inserted && user != null)
-            _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user.Value):player} inserted {ToPrettyString(inserted)} into {ToPrettyString(uid)}");
-
-        QueueAutomaticEngage(uid, component);
-
-        _ui.CloseUi(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key, inserted);
-
-        // Maybe do pullable instead? Eh still fine.
-        Joints.RecursiveClearJoints(inserted);
-        UpdateVisualState(uid, component);
+        switch (args.Button)
+        {
+            case DisposalUnitComponent.UiButton.Eject:
+                TryEjectContents(uid, component);
+                _adminLog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit eject button on {ToPrettyString(uid)}");
+                break;
+            case DisposalUnitComponent.UiButton.Engage:
+                ToggleEngage(uid, component);
+                _adminLog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(player):player} hit flush button on {ToPrettyString(uid)}, it's now {(component.Engaged ? "on" : "off")}");
+                break;
+            case DisposalUnitComponent.UiButton.Power:
+                _power.TogglePower(uid, user: args.Actor);
+                break;
+            default:
+                throw new ArgumentOutOfRangeException($"{ToPrettyString(player):player} attempted to hit a nonexistant button on {ToPrettyString(uid)}");
+        }
     }
 
-    private void OnExploded(Entity<DisposalUnitComponent> ent, ref BeforeExplodeEvent args)
+    public void ToggleEngage(EntityUid uid, DisposalUnitComponent component)
     {
-        args.Contents.AddRange(ent.Comp.Container.ContainedEntities);
+        component.Engaged ^= true;
+
+        if (component.Engaged)
+        {
+            ManualEngage(uid, component);
+        }
+        else
+        {
+            Disengage(uid, component);
+        }
     }
 
-}
+    private void AddClimbInsideVerb(EntityUid uid, DisposalUnitComponent component, GetVerbsEvent<Verb> args)
+    {
+        // This is not an interaction, activation, or alternative verb type because unfortunately most users are
+        // unwilling to accept that this is where they belong and don't want to accidentally climb inside.
+        if (!args.CanAccess ||
+            !args.CanInteract ||
+            component.Container.ContainedEntities.Contains(args.User) ||
+            !ActionBlockerSystem.CanMove(args.User))
+        {
+            return;
+        }
 
-/// <summary>
-/// Sent before the disposal unit flushes it's contents.
-/// Allows adding tags for sorting and preventing the disposal unit from flushing.
-/// </summary>
-public sealed class DisposalUnitUIStateUpdatedEvent : EntityEventArgs
-{
-    public SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState State;
+        if (!CanInsert(uid, component, args.User))
+            return;
 
-    public DisposalUnitUIStateUpdatedEvent(SharedDisposalUnitComponent.DisposalUnitBoundUserInterfaceState state)
-    {
-        State = state;
+        // Add verb to climb inside of the unit,
+        Verb verb = new()
+        {
+            Act = () => TryInsert(uid, args.User, args.User),
+            DoContactInteraction = true,
+            Text = Loc.GetString("disposal-self-insert-verb-get-data-text")
+        };
+        // TODO VERB ICON
+        // TODO VERB CATEGORY
+        // create a verb category for "enter"?
+        // See also, medical scanner. Also maybe add verbs for entering lockers/body bags?
+        args.Verbs.Add(verb);
     }
 }
-
-/// <summary>
-/// Sent before the disposal unit flushes it's contents.
-/// Allows adding tags for sorting and preventing the disposal unit from flushing.
-/// </summary>
-public sealed class BeforeDisposalFlushEvent : CancellableEntityEventArgs
-{
-    public readonly List<string> Tags = new();
-}
index 69c3bd790e57db2551df115d0faa7e4dd0229a80..80bacd24bd609a8286be9dd95d9c82e955a0cf76 100644 (file)
@@ -8,9 +8,15 @@ public abstract partial class SharedApcPowerReceiverComponent : Component
     [ViewVariables]
     public bool Powered;
 
-    [ViewVariables]
-    public virtual bool NeedsPower { get; set; }
+    /// <summary>
+    ///     When false, causes this to appear powered even if not receiving power from an Apc.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite)]
+    public virtual bool NeedsPower { get; set;}
 
-    [ViewVariables]
+    /// <summary>
+    ///     When true, causes this to never appear powered.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite)]
     public virtual bool PowerDisabled { get; set; }
 }
index 2d152d8b45e235775636e6bff90691776a6c5e33..d86273974bbcabc2653eb1952892e0952b2a3707 100644 (file)
@@ -62,8 +62,13 @@ public abstract class SharedPowerReceiverSystem : EntitySystem
         return !receiver.PowerDisabled; // i.e. PowerEnabled
     }
 
-    /// <summary>
-    /// Checks if entity is APC-powered device, and if it have power.
+    protected virtual void RaisePower(Entity<SharedApcPowerReceiverComponent> entity)
+    {
+        // NOOP on server because client has 0 idea of load so we can't raise it properly in shared.
+    }
+
+       /// <summary>
+       /// Checks if entity is APC-powered device, and if it have power.
     /// </summary>
     public bool IsPowered(Entity<SharedApcPowerReceiverComponent?> entity)
     {
index 93c4b69e4ddd571a46307a8b6187e958fb74e8c2..d0ad27eee5a3da5a52c02a3158c66951a99d18a8 100644 (file)
@@ -1,5 +1,7 @@
 using System.Linq;
 using Content.Shared.Disposal;
+using Content.Shared.Disposal.Components;
+using Content.Shared.Disposal.Unit;
 using Content.Shared.DoAfter;
 using Content.Shared.Interaction;
 using Content.Shared.Item;
@@ -40,7 +42,7 @@ public sealed class DumpableSystem : EntitySystem
         if (!args.CanReach || args.Handled)
             return;
 
-        if (!_disposalUnitSystem.HasDisposals(args.Target) && !HasComp<PlaceableSurfaceComponent>(args.Target))
+        if (!HasComp<DisposalUnitComponent>(args.Target) && !HasComp<PlaceableSurfaceComponent>(args.Target))
             return;
 
         if (!TryComp<StorageComponent>(uid, out var storage))
@@ -81,7 +83,7 @@ public sealed class DumpableSystem : EntitySystem
         if (!TryComp<StorageComponent>(uid, out var storage) || !storage.Container.ContainedEntities.Any())
             return;
 
-        if (_disposalUnitSystem.HasDisposals(args.Target))
+        if (HasComp<DisposalUnitComponent>(args.Target))
         {
             UtilityVerb verb = new()
             {
@@ -146,7 +148,7 @@ public sealed class DumpableSystem : EntitySystem
 
         var dumped = false;
 
-        if (_disposalUnitSystem.HasDisposals(args.Args.Target))
+        if (HasComp<DisposalUnitComponent>(args.Args.Target))
         {
             dumped = true;
 
index cb3ae3d06562bca2f4389831b1b0fa12259b433d..2605af80198941d3c39e414b613cfbf04bce227b 100644 (file)
@@ -32,8 +32,6 @@
       components:
       - HumanoidAppearance
   - type: DisposalUnit
-    autoEngageEnabled: false
-    noUI: true
     blacklist:
       components:
       - HumanoidAppearance
index 9d04b32563ed77030ef6404466823e4fa7c20d84..7388a814f752a8dfc06b01e8e379a41b86a6baf7 100644 (file)
@@ -8,6 +8,8 @@
     snap:
     - Disposal
   components:
+  - type: Climbable
+    vaultable: false
   - type: Sprite
     sprite: Structures/Piping/disposal.rsi
     layers:
       map: [ "enum.DisposalUnitVisualLayers.OverlayEngaged" ]
   - type: Physics
     bodyType: Static
+  - type: Fixtures
+    fixtures:
+      fix1:
+        shape:
+          !type:PhysShapeAabb
+          bounds: "-0.45,-0.45,0.45,0.45"
+        density: 55
+        mask:
+        - TableMask
+        layer:
+        - TableLayer
   - type: Destructible
     thresholds:
     - trigger:
     graph: DisposalMachine
     node: mailing_unit
   - type: DisposalUnit
-    autoEngageEnabled: false
     whitelist:
       components:
       - Item
   - type: UserInterface
     interfaces:
       enum.MailingUnitUiKey.Key:
-        type: DisposalUnitBoundUserInterface
+        type: MailingUnitBoundUserInterface
       enum.ConfigurationUiKey.Key:
         type: ConfigurationBoundUserInterface