diff --git a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs index 2a531e9a41..7f950439cc 100644 --- a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs +++ b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs @@ -9,12 +9,12 @@ 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 SoundSystem = default!; + [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 List PressuringDisposals = new(); + private readonly List _pressuringDisposals = new(); public override void Initialize() { @@ -28,25 +28,25 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem { if (active) { - if (!PressuringDisposals.Contains(disposalEntity)) - PressuringDisposals.Add(disposalEntity); + if (!_pressuringDisposals.Contains(disposalEntity)) + _pressuringDisposals.Add(disposalEntity); } else { - PressuringDisposals.Remove(disposalEntity); + _pressuringDisposals.Remove(disposalEntity); } } public override void FrameUpdate(float frameTime) { base.FrameUpdate(frameTime); - for (var i = PressuringDisposals.Count - 1; i >= 0; i--) + for (var i = _pressuringDisposals.Count - 1; i >= 0; i--) { - var disposal = PressuringDisposals[i]; + var disposal = _pressuringDisposals[i]; if (!UpdateInterface(disposal)) continue; - PressuringDisposals.RemoveAt(i); + _pressuringDisposals.RemoveAt(i); } } @@ -79,17 +79,18 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem if (!TryComp(uid, out var sprite)) return; - if(!sprite.LayerMapTryGet(DisposalUnitVisualLayers.Base, out var baseLayerIdx)) + if (!sprite.LayerMapTryGet(DisposalUnitVisualLayers.Base, out var baseLayerIdx)) return; // Couldn't find the "normal" layer to return to after flush animation - if(!sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseFlush, out var flushLayerIdx)) + if (!sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseFlush, out var flushLayerIdx)) return; // Couldn't find the flush animation layer var originalBaseState = sprite.LayerGetState(baseLayerIdx); var flushState = sprite.LayerGetState(flushLayerIdx); // Setup the flush animation to play - disposalUnit.FlushAnimation = new Animation { + disposalUnit.FlushAnimation = new Animation + { Length = TimeSpan.FromSeconds(disposalUnit.FlushTime), AnimationTracks = { new AnimationTrackSpriteFlick { @@ -109,9 +110,10 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem if (disposalUnit.FlushSound != null) { disposalUnit.FlushAnimation.AnimationTracks.Add( - new AnimationTrackPlaySound { + new AnimationTrackPlaySound + { KeyFrames = { - new AnimationTrackPlaySound.KeyFrame(SoundSystem.GetSound(disposalUnit.FlushSound), 0) + new AnimationTrackPlaySound.KeyFrame(_audioSystem.GetSound(disposalUnit.FlushSound), 0) } }); } @@ -134,7 +136,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem // Update visuals and tick animation private void UpdateState(EntityUid uid, DisposalUnitComponent unit, SpriteComponent sprite) { - if (!AppearanceSystem.TryGetData(uid, Visuals.VisualState, out var state)) + if (!_appearanceSystem.TryGetData(uid, Visuals.VisualState, out var state)) { return; } @@ -146,20 +148,20 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem if (state == VisualState.Flushing) { - if (!AnimationSystem.HasRunningAnimation(uid, AnimationKey)) + if (!_animationSystem.HasRunningAnimation(uid, AnimationKey)) { - AnimationSystem.Play(uid, unit.FlushAnimation, AnimationKey); + _animationSystem.Play(uid, unit.FlushAnimation, AnimationKey); } } - if (!AppearanceSystem.TryGetData(uid, Visuals.Handle, out var handleState)) + if (!_appearanceSystem.TryGetData(uid, Visuals.Handle, out var handleState)) { handleState = HandleState.Normal; } sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != HandleState.Normal); - if (!AppearanceSystem.TryGetData(uid, Visuals.Light, out var lightState)) + if (!_appearanceSystem.TryGetData(uid, Visuals.Light, out var lightState)) { lightState = LightStates.Off; } diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs index 4d8616bd81..3c9fdddb93 100644 --- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs +++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs @@ -8,7 +8,6 @@ using Content.Server.Power.Components; using Content.Shared.Disposal; using NUnit.Framework; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.Reflection; namespace Content.IntegrationTests.Tests.Disposal @@ -33,22 +32,20 @@ namespace Content.IntegrationTests.Tests.Disposal var unitTransform = EntityManager.GetComponent(unit); // Not in a tube yet Assert.That(insertTransform.ParentUid, Is.EqualTo(unit)); - }, after: new[] {typeof(SharedDisposalUnitSystem)}); + }, after: new[] { typeof(SharedDisposalUnitSystem) }); } } - private void UnitInsert(DisposalUnitComponent unit, bool result, params EntityUid[] entities) + private static void UnitInsert(EntityUid uid, DisposalUnitComponent unit, bool result, DisposalUnitSystem disposalSystem, params EntityUid[] entities) { - var system = EntitySystem.Get(); - foreach (var entity in entities) { - Assert.That(system.CanInsert(unit, entity), Is.EqualTo(result)); - system.TryInsert(unit.Owner, entity, null); + Assert.That(disposalSystem.CanInsert(uid, unit, entity), Is.EqualTo(result)); + disposalSystem.TryInsert(uid, entity, null); } } - private void UnitContains(DisposalUnitComponent unit, bool result, params EntityUid[] entities) + private static void UnitContains(DisposalUnitComponent unit, bool result, params EntityUid[] entities) { foreach (var entity in entities) { @@ -56,19 +53,22 @@ namespace Content.IntegrationTests.Tests.Disposal } } - private void UnitInsertContains(DisposalUnitComponent unit, bool result, params EntityUid[] entities) + private static void UnitInsertContains(EntityUid uid, DisposalUnitComponent unit, bool result, DisposalUnitSystem disposalSystem, params EntityUid[] entities) { - UnitInsert(unit, result, entities); + UnitInsert(uid, unit, result, disposalSystem, entities); UnitContains(unit, result, entities); } - private void Flush(EntityUid unitEntity, DisposalUnitComponent unit, bool result, params EntityUid[] entities) + private static void Flush(EntityUid unitEntity, DisposalUnitComponent unit, bool result, DisposalUnitSystem disposalSystem, params EntityUid[] entities) { - Assert.That(unit.Container.ContainedEntities, Is.SupersetOf(entities)); - Assert.That(entities.Length, Is.EqualTo(unit.Container.ContainedEntities.Count)); + Assert.Multiple(() => + { + Assert.That(unit.Container.ContainedEntities, Is.SupersetOf(entities)); + Assert.That(entities, Has.Length.EqualTo(unit.Container.ContainedEntities.Count)); - Assert.That(result, Is.EqualTo(EntitySystem.Get().TryFlush(unitEntity, unit))); - Assert.That(result || entities.Length == 0, Is.EqualTo(unit.Container.ContainedEntities.Count == 0)); + Assert.That(result, Is.EqualTo(disposalSystem.TryFlush(unitEntity, unit))); + Assert.That(result || entities.Length == 0, Is.EqualTo(unit.Container.ContainedEntities.Count == 0)); + }); } private const string Prototypes = @" @@ -147,7 +147,10 @@ namespace Content.IntegrationTests.Tests.Disposal public async Task Test() { await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings - {NoClient = true, ExtraPrototypes = Prototypes}); + { + NoClient = true, + ExtraPrototypes = Prototypes + }); var server = pairTracker.Pair.Server; var testMap = await PoolManager.CreateTestMap(pairTracker); @@ -161,6 +164,8 @@ namespace Content.IntegrationTests.Tests.Disposal DisposalUnitComponent unitComponent = default!; var entityManager = server.ResolveDependency(); + var xformSystem = entityManager.System(); + var disposalSystem = entityManager.System(); await server.WaitAssertion(() => { @@ -174,62 +179,69 @@ namespace Content.IntegrationTests.Tests.Disposal // Test for components existing unitUid = disposalUnit; - Assert.True(entityManager.TryGetComponent(disposalUnit, out unitComponent)); - Assert.True(entityManager.HasComponent(disposalTrunk)); + Assert.Multiple(() => + { + Assert.That(entityManager.TryGetComponent(disposalUnit, out unitComponent)); + Assert.That(entityManager.HasComponent(disposalTrunk)); + }); // Can't insert, unanchored and unpowered - entityManager.GetComponent(unitUid).Anchored = false; - UnitInsertContains(unitComponent, false, human, wrench, disposalUnit, disposalTrunk); + xformSystem.Unanchor(unitUid, entityManager.GetComponent(unitUid)); + UnitInsertContains(disposalUnit, unitComponent, false, disposalSystem, human, wrench, disposalUnit, disposalTrunk); }); await server.WaitAssertion(() => { // Anchor the disposal unit - entityManager.GetComponent(unitUid).Anchored = true; + xformSystem.AnchorEntity(unitUid, entityManager.GetComponent(unitUid)); // No power - Assert.False(unitComponent.Powered); + Assert.That(unitComponent.Powered, Is.False); // Can't insert the trunk or the unit into itself - UnitInsertContains(unitComponent, false, disposalUnit, disposalTrunk); + UnitInsertContains(unitUid, unitComponent, false, disposalSystem, disposalUnit, disposalTrunk); // Can insert mobs and items - UnitInsertContains(unitComponent, true, human, wrench); + UnitInsertContains(unitUid, unitComponent, true, disposalSystem, human, wrench); }); await server.WaitAssertion(() => { // Move the disposal trunk away - entityManager.GetComponent(disposalTrunk).WorldPosition += (1, 0); + var xform = entityManager.GetComponent(disposalTrunk); + var worldPos = xformSystem.GetWorldPosition(disposalTrunk); + xformSystem.SetWorldPosition(xform, worldPos + (1, 0)); // Fail to flush with a mob and an item - Flush(disposalUnit, unitComponent, false, human, wrench); + Flush(disposalUnit, unitComponent, false, disposalSystem, human, wrench); }); await server.WaitAssertion(() => { // Move the disposal trunk back - entityManager.GetComponent(disposalTrunk).WorldPosition -= (1, 0); + var xform = entityManager.GetComponent(disposalTrunk); + var worldPos = xformSystem.GetWorldPosition(disposalTrunk); + xformSystem.SetWorldPosition(xform, worldPos - (1, 0)); // Fail to flush with a mob and an item, no power - Flush(disposalUnit, unitComponent, false, human, wrench); + Flush(disposalUnit, unitComponent, false, disposalSystem, human, wrench); }); await server.WaitAssertion(() => { // Remove power need - Assert.True(entityManager.TryGetComponent(disposalUnit, out ApcPowerReceiverComponent power)); + Assert.That(entityManager.TryGetComponent(disposalUnit, out ApcPowerReceiverComponent power)); power!.NeedsPower = false; unitComponent.Powered = true; //Power state changed event doesn't get fired smh // Flush with a mob and an item - Flush(disposalUnit, unitComponent, true, human, wrench); + Flush(disposalUnit, unitComponent, true, disposalSystem, human, wrench); }); await server.WaitAssertion(() => { // Re-pressurizing - Flush(disposalUnit, unitComponent, false); + Flush(disposalUnit, unitComponent, false, disposalSystem); }); await pairTracker.CleanReturnAsync(); } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs index df63727532..f5b06a5f0c 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs @@ -7,7 +7,6 @@ using Content.Shared.Maps; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Timing; -using static Content.Shared.Disposal.Components.SharedDisposalUnitComponent; namespace Content.Server.Atmos.EntitySystems { diff --git a/Content.Server/Disposal/Mailing/MailingUnitSystem.cs b/Content.Server/Disposal/Mailing/MailingUnitSystem.cs index ebb9c69d4f..bc70bcb6dc 100644 --- a/Content.Server/Disposal/Mailing/MailingUnitSystem.cs +++ b/Content.Server/Disposal/Mailing/MailingUnitSystem.cs @@ -58,7 +58,7 @@ public sealed class MailingUnitSystem : EntitySystem 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(component); + UpdateUserInterface(uid, component); break; } } @@ -146,7 +146,7 @@ public sealed class MailingUnitSystem : EntitySystem } component.Tag = configuration[TagConfigurationKey]; - UpdateUserInterface(component); + UpdateUserInterface(uid, component); } private void HandleActivate(EntityUid uid, MailingUnitComponent component, ActivateInWorldEvent args) @@ -158,7 +158,8 @@ public sealed class MailingUnitSystem : EntitySystem args.Handled = true; UpdateTargetList(uid, component); - _userInterfaceSystem.GetUiOrNull(uid, MailingUnitUiKey.Key)?.Open(actor.PlayerSession); + if (_userInterfaceSystem.TryGetUi(uid, MailingUnitUiKey.Key, out var bui)) + _userInterfaceSystem.OpenUi(bui, actor.PlayerSession); } /// @@ -167,28 +168,23 @@ public sealed class MailingUnitSystem : EntitySystem private void OnDisposalUnitUIStateChange(EntityUid uid, MailingUnitComponent component, DisposalUnitUIStateUpdatedEvent args) { component.DisposalUnitInterfaceState = args.State; - UpdateUserInterface(component); + UpdateUserInterface(uid, component); } - private void UpdateUserInterface(MailingUnitComponent component) + private void UpdateUserInterface(EntityUid uid, MailingUnitComponent component) { if (component.DisposalUnitInterfaceState == null) return; var state = new MailingUnitBoundUserInterfaceState(component.DisposalUnitInterfaceState, component.Target, component.TargetList, component.Tag); - component.Owner.GetUIOrNull(MailingUnitUiKey.Key)?.SetState(state); + if (_userInterfaceSystem.TryGetUi(uid, MailingUnitUiKey.Key, out var bui)) + _userInterfaceSystem.SetUiState(bui, state); } private void OnTargetSelected(EntityUid uid, MailingUnitComponent component, TargetSelectedMessage args) { - if (string.IsNullOrEmpty(args.target)) - { - component.Target = null; - } - - component.Target = args.target; - UpdateUserInterface(component); - + component.Target = args.Target; + UpdateUserInterface(uid, component); } /// diff --git a/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs index a9fc1ae4cf..3223de35db 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs +++ b/Content.Server/Disposal/Tube/Components/DisposalEntryComponent.cs @@ -1,6 +1,3 @@ -using System.Linq; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Disposal.Unit.Components; using Content.Server.Disposal.Unit.EntitySystems; namespace Content.Server.Disposal.Tube.Components @@ -9,27 +6,6 @@ namespace Content.Server.Disposal.Tube.Components [Access(typeof(DisposalTubeSystem), typeof(DisposalUnitSystem))] public sealed class DisposalEntryComponent : Component { - [Dependency] private readonly IEntityManager _entMan = default!; - - private const string HolderPrototypeId = "DisposalHolder"; - - public bool TryInsert(DisposalUnitComponent from, IEnumerable? tags = default) - { - var holder = _entMan.SpawnEntity(HolderPrototypeId, _entMan.GetComponent(Owner).MapPosition); - var holderComponent = _entMan.GetComponent(holder); - - foreach (var entity in from.Container.ContainedEntities.ToArray()) - { - holderComponent.TryInsert(entity); - } - - EntitySystem.Get().Merge(holderComponent.Air, from.Air); - from.Air.Clear(); - - if (tags != default) - holderComponent.Tags.UnionWith(tags); - - return EntitySystem.Get().EnterTube((holderComponent).Owner, Owner, holderComponent); - } + public const string HolderPrototypeId = "DisposalHolder"; } } diff --git a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs index 1f5214282f..6ab7e3d3be 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs +++ b/Content.Server/Disposal/Tube/Components/DisposalRouterComponent.cs @@ -17,63 +17,7 @@ namespace Content.Server.Disposal.Tube.Components [DataField("tags")] public HashSet Tags = new(); - [ViewVariables] - public bool Anchored => - !_entMan.TryGetComponent(Owner, out PhysicsComponent? physics) || - physics.BodyType == BodyType.Static; - - [ViewVariables] public BoundUserInterface? UserInterface => Owner.GetUIOrNull(DisposalRouterUiKey.Key); - - [DataField("clickSound")] private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); - - protected override void Initialize() - { - base.Initialize(); - - if (UserInterface != null) - { - UserInterface.OnReceiveMessage += OnUiReceiveMessage; - } - } - - /// - /// Handles ui messages from the client. For things such as button presses - /// which interact with the world and require server action. - /// - /// A user interface message from the client. - private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj) - { - if (obj.Session.AttachedEntity == null) - { - return; - } - - var msg = (UiActionMessage) obj.Message; - - if (!Anchored) - return; - - //Check for correct message and ignore maleformed strings - if (msg.Action == UiAction.Ok && TagRegex.IsMatch(msg.Tags)) - { - Tags.Clear(); - foreach (var tag in msg.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries)) - { - Tags.Add(tag.Trim()); - ClickSound(); - } - } - } - - private void ClickSound() - { - SoundSystem.Play(_clickSound.GetSound(), Filter.Pvs(Owner), Owner, AudioParams.Default.WithVolume(-2f)); - } - - protected override void OnRemove() - { - UserInterface?.CloseAll(); - base.OnRemove(); - } + [DataField("clickSound")] + public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); } } diff --git a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs index 1c37ae25cc..a1fbade5e7 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs +++ b/Content.Server/Disposal/Tube/Components/DisposalTaggerComponent.cs @@ -12,60 +12,11 @@ namespace Content.Server.Disposal.Tube.Components [RegisterComponent] public sealed class DisposalTaggerComponent : DisposalTransitComponent { - [Dependency] private readonly IEntityManager _entMan = default!; - [ViewVariables(VVAccess.ReadWrite)] [DataField("tag")] public string Tag = ""; - [ViewVariables] - public bool Anchored => - !_entMan.TryGetComponent(Owner, out PhysicsComponent? physics) || - physics.BodyType == BodyType.Static; - - [ViewVariables] public BoundUserInterface? UserInterface => Owner.GetUIOrNull(DisposalTaggerUiKey.Key); - - [DataField("clickSound")] private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); - - protected override void Initialize() - { - base.Initialize(); - - if (UserInterface != null) - { - UserInterface.OnReceiveMessage += OnUiReceiveMessage; - } - } - - /// - /// Handles ui messages from the client. For things such as button presses - /// which interact with the world and require server action. - /// - /// A user interface message from the client. - private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj) - { - var msg = (UiActionMessage) obj.Message; - - if (!Anchored) - return; - - //Check for correct message and ignore maleformed strings - if (msg.Action == UiAction.Ok && TagRegex.IsMatch(msg.Tag)) - { - Tag = msg.Tag; - ClickSound(); - } - } - - private void ClickSound() - { - SoundSystem.Play(_clickSound.GetSound(), Filter.Pvs(Owner), Owner, AudioParams.Default.WithVolume(-2f)); - } - - protected override void OnRemove() - { - base.OnRemove(); - UserInterface?.CloseAll(); - } + [DataField("clickSound")] + public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); } } diff --git a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs b/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs index f6bfc00a63..c673e9cca5 100644 --- a/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs +++ b/Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs @@ -26,53 +26,7 @@ namespace Content.Server.Disposal.Tube.Components /// Container of entities that are currently inside this tube /// [ViewVariables] - public Container Contents { get; private set; } = default!; - - // TODO: Make disposal pipes extend the grid - // ??? - public void Connect() - { - if (Connected) - { - return; - } - - Connected = true; - } - - public void Disconnect() - { - if (!Connected) - { - return; - } - - Connected = false; - - foreach (var entity in Contents.ContainedEntities.ToArray()) - { - if (!_entMan.TryGetComponent(entity, out DisposalHolderComponent? holder)) - { - continue; - } - - EntitySystem.Get().ExitDisposals((holder).Owner); - } - } - - protected override void Initialize() - { - base.Initialize(); - - Contents = ContainerHelpers.EnsureContainer(Owner, ContainerId); - Owner.EnsureComponent(); - } - - protected override void OnRemove() - { - base.OnRemove(); - - Disconnect(); - } + [Access(typeof(DisposalTubeSystem), typeof(DisposableSystem))] + public Container Contents { get; set; } = default!; } } diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs index aea901ee1f..8bd31ce2d6 100644 --- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs @@ -1,19 +1,26 @@ 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.Popups; using Content.Server.UserInterface; using Content.Shared.Destructible; using Content.Shared.Disposal.Components; using Content.Shared.Hands.Components; using Content.Shared.Movement.Events; -using Content.Shared.Popups; +using Robust.Server.GameObjects; using Robust.Shared.Audio; +using Robust.Shared.Containers; using Robust.Shared.Map; -using Robust.Shared.Player; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; using Robust.Shared.Random; using Robust.Shared.Timing; +using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; +using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; namespace Content.Server.Disposal.Tube { @@ -24,11 +31,18 @@ namespace Content.Server.Disposal.Tube [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly PopupSystem _popups = default!; - + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly DisposableSystem _disposableSystem = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnComponentRemove); + SubscribeLocalEvent(OnAnchorChange); SubscribeLocalEvent(OnRelayMovement); SubscribeLocalEvent(OnBreak); @@ -44,17 +58,90 @@ namespace Content.Server.Disposal.Tube SubscribeLocalEvent(OnGetJunctionConnectableDirections); SubscribeLocalEvent(OnGetJunctionNextDirection); + SubscribeLocalEvent(OnComponentRemove); SubscribeLocalEvent(OnGetRouterConnectableDirections); SubscribeLocalEvent(OnGetRouterNextDirection); SubscribeLocalEvent(OnGetTransitConnectableDirections); SubscribeLocalEvent(OnGetTransitNextDirection); + SubscribeLocalEvent(OnComponentRemove); SubscribeLocalEvent(OnGetTaggerConnectableDirections); SubscribeLocalEvent(OnGetTaggerNextDirection); SubscribeLocalEvent(OnOpenRouterUIAttempt); SubscribeLocalEvent(OnOpenTaggerUIAttempt); + + SubscribeLocalEvent(OnUiAction); + SubscribeLocalEvent(OnUiAction); + } + + + /// + /// Handles ui messages from the client. For things such as button presses + /// which interact with the world and require server action. + /// + /// A user interface message from the client. + private void OnUiAction(EntityUid uid, DisposalTaggerComponent tagger, SharedDisposalTaggerComponent.UiActionMessage msg) + { + if (!DisposalTaggerUiKey.Key.Equals(msg.UiKey)) + return; + if (TryComp(uid, out var physBody) && physBody.BodyType != BodyType.Static) + return; + + //Check for correct message and ignore maleformed strings + if (msg.Action == SharedDisposalTaggerComponent.UiAction.Ok && SharedDisposalTaggerComponent.TagRegex.IsMatch(msg.Tag)) + { + tagger.Tag = msg.Tag; + _audioSystem.PlayPvs(tagger.ClickSound, uid, AudioParams.Default.WithVolume(-2f)); + } + } + + + /// + /// Handles ui messages from the client. For things such as button presses + /// which interact with the world and require server action. + /// + /// A user interface message from the client. + private void OnUiAction(EntityUid uid, DisposalRouterComponent router, SharedDisposalRouterComponent.UiActionMessage msg) + { + if (!DisposalRouterUiKey.Key.Equals(msg.UiKey)) + return; + if (!EntityManager.EntityExists(msg.Session.AttachedEntity)) + return; + if (TryComp(uid, out var physBody) && physBody.BodyType != BodyType.Static) + return; + + //Check for correct message and ignore maleformed strings + if (msg.Action == SharedDisposalRouterComponent.UiAction.Ok && SharedDisposalRouterComponent.TagRegex.IsMatch(msg.Tags)) + { + router.Tags.Clear(); + foreach (var tag in msg.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries)) + { + router.Tags.Add(tag.Trim()); + _audioSystem.PlayPvs(router.ClickSound, uid, AudioParams.Default.WithVolume(-2f)); + } + } + } + + private void OnComponentInit(EntityUid uid, DisposalTubeComponent tube, ComponentInit args) + { + tube.Contents = _containerSystem.EnsureContainer(uid, tube.ContainerId); + } + + private void OnComponentRemove(EntityUid uid, DisposalTubeComponent tube, ComponentRemove args) + { + DisconnectTube(uid, tube); + } + + private void OnComponentRemove(EntityUid uid, DisposalTaggerComponent tagger, ComponentRemove args) + { + _uiSystem.TryCloseAll(uid, DisposalTaggerUiKey.Key); + } + + private void OnComponentRemove(EntityUid uid, DisposalRouterComponent tagger, ComponentRemove args) + { + _uiSystem.TryCloseAll(uid, DisposalRouterUiKey.Key); } private void OnGetBendConnectableDirections(EntityUid uid, DisposalBendComponent component, ref GetDisposalsConnectableDirectionsEvent args) @@ -62,7 +149,7 @@ namespace Content.Server.Disposal.Tube var direction = Transform(uid).LocalRotation; var side = new Angle(MathHelper.DegreesToRadians(direction.Degrees - 90)); - args.Connectable = new[] {direction.GetDir(), side.GetDir()}; + args.Connectable = new[] { direction.GetDir(), side.GetDir() }; } private void OnGetBendNextDirection(EntityUid uid, DisposalBendComponent component, ref GetDisposalsNextDirectionEvent args) @@ -83,7 +170,7 @@ namespace Content.Server.Disposal.Tube private void OnGetEntryConnectableDirections(EntityUid uid, DisposalEntryComponent component, ref GetDisposalsConnectableDirectionsEvent args) { - args.Connectable = new[] {Transform(uid).LocalRotation.GetDir()}; + args.Connectable = new[] { Transform(uid).LocalRotation.GetDir() }; } private void OnGetEntryNextDirection(EntityUid uid, DisposalEntryComponent component, ref GetDisposalsNextDirectionEvent args) @@ -150,7 +237,7 @@ namespace Content.Server.Disposal.Tube var rotation = Transform(uid).LocalRotation; var opposite = new Angle(rotation.Theta + Math.PI); - args.Connectable = new[] {rotation.GetDir(), opposite.GetDir()}; + args.Connectable = new[] { rotation.GetDir(), opposite.GetDir() }; } private void OnGetTransitNextDirection(EntityUid uid, DisposalTransitComponent component, ref GetDisposalsNextDirectionEvent args) @@ -183,7 +270,7 @@ namespace Content.Server.Disposal.Tube private void OnDeconstruct(EntityUid uid, DisposalTubeComponent component, ConstructionBeforeDeleteEvent args) { - component.Disconnect(); + DisconnectTube(uid, component); } private void OnStartup(EntityUid uid, DisposalTubeComponent component, ComponentStartup args) @@ -199,19 +286,19 @@ namespace Content.Server.Disposal.Tube } component.LastClang = _gameTiming.CurTime; - SoundSystem.Play(component.ClangSound.GetSound(), Filter.Pvs(uid), uid); + _audioSystem.PlayPvs(component.ClangSound, uid); } private void OnBreak(EntityUid uid, DisposalTubeComponent component, BreakageEventArgs args) { - component.Disconnect(); + DisconnectTube(uid, component); } private void OnOpenRouterUIAttempt(EntityUid uid, DisposalRouterComponent router, ActivatableUIOpenAttemptEvent args) { if (!TryComp(args.User, out var hands)) { - uid.PopupMessage(args.User, Loc.GetString("disposal-router-window-tag-input-activate-no-hands")); + _popups.PopupClient(Loc.GetString("disposal-router-window-tag-input-activate-no-hands"), uid, args.User); return; } @@ -221,14 +308,14 @@ namespace Content.Server.Disposal.Tube args.Cancel(); } - UpdateRouterUserInterface(router); + UpdateRouterUserInterface(uid, router); } private void OnOpenTaggerUIAttempt(EntityUid uid, DisposalTaggerComponent tagger, ActivatableUIOpenAttemptEvent args) { if (!TryComp(args.User, out var hands)) { - uid.PopupMessage(args.User, Loc.GetString("disposal-tagger-window-activate-no-hands")); + _popups.PopupClient(Loc.GetString("disposal-tagger-window-activate-no-hands"), uid, args.User); return; } @@ -238,18 +325,21 @@ namespace Content.Server.Disposal.Tube args.Cancel(); } - tagger.UserInterface?.SetState(new SharedDisposalTaggerComponent.DisposalTaggerUserInterfaceState(tagger.Tag)); + if (_uiSystem.TryGetUi(uid, SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key, out var bui)) + _uiSystem.SetUiState(bui, new SharedDisposalTaggerComponent.DisposalTaggerUserInterfaceState(tagger.Tag)); } /// /// Gets component data to be used to update the user interface client-side. /// /// Returns a - private void UpdateRouterUserInterface(DisposalRouterComponent router) + private void UpdateRouterUserInterface(EntityUid uid, DisposalRouterComponent router) { + var bui = _uiSystem.GetUiOrNull(uid, SharedDisposalTaggerComponent.DisposalTaggerUiKey.Key); if (router.Tags.Count <= 0) { - router.UserInterface?.SetState(new SharedDisposalRouterComponent.DisposalRouterUserInterfaceState("")); + if (bui is not null) + _uiSystem.SetUiState(bui, new SharedDisposalTaggerComponent.DisposalTaggerUserInterfaceState("")); return; } @@ -263,7 +353,8 @@ namespace Content.Server.Disposal.Tube taglist.Remove(taglist.Length - 2, 2); - router.UserInterface?.SetState(new SharedDisposalRouterComponent.DisposalRouterUserInterfaceState(taglist.ToString())); + if (bui is not null) + _uiSystem.SetUiState(bui, new SharedDisposalTaggerComponent.DisposalTaggerUserInterfaceState(taglist.ToString())); } private void OnAnchorChange(EntityUid uid, DisposalTubeComponent component, ref AnchorStateChangedEvent args) @@ -275,25 +366,25 @@ namespace Content.Server.Disposal.Tube { if (anchored) { - component.Connect(); + ConnectTube(uid, component); // TODO this visual data should just generalized into some anchored-visuals system/comp, this has nothing to do with disposal tubes. _appearanceSystem.SetData(uid, DisposalTubeVisuals.VisualState, DisposalTubeVisualState.Anchored); } else { - component.Disconnect(); + DisconnectTube(uid, component); _appearanceSystem.SetData(uid, DisposalTubeVisuals.VisualState, DisposalTubeVisualState.Free); } } - public DisposalTubeComponent? NextTubeFor(EntityUid target, Direction nextDirection, DisposalTubeComponent? targetTube = null) + public EntityUid? NextTubeFor(EntityUid target, Direction nextDirection, DisposalTubeComponent? targetTube = null) { if (!Resolve(target, ref targetTube)) return null; var oppositeDirection = nextDirection.GetOpposite(); - var xform = Transform(targetTube.Owner); + var xform = Transform(target); if (!_mapManager.TryGetGrid(xform.GridUid, out var grid)) return null; @@ -315,12 +406,39 @@ namespace Content.Server.Disposal.Tube continue; } - return tube; + return entity; } return null; } + public static void ConnectTube(EntityUid _, DisposalTubeComponent tube) + { + if (tube.Connected) + { + return; + } + + tube.Connected = true; + } + + + public void DisconnectTube(EntityUid _, DisposalTubeComponent tube) + { + if (!tube.Connected) + { + return; + } + + tube.Connected = false; + + var query = GetEntityQuery(); + foreach (var entity in tube.Contents.ContainedEntities.ToArray()) + { + if (query.TryGetComponent(entity, out var holder)) + _disposableSystem.ExitDisposals(entity, holder); + } + } public bool CanConnect(EntityUid tubeId, DisposalTubeComponent tube, Direction direction) { @@ -334,7 +452,7 @@ namespace Content.Server.Disposal.Tube return ev.Connectable.Contains(direction); } - public void PopupDirections(EntityUid tubeId, DisposalTubeComponent tube, EntityUid recipient) + public void PopupDirections(EntityUid tubeId, DisposalTubeComponent _, EntityUid recipient) { var ev = new GetDisposalsConnectableDirectionsEvent(); RaiseLocalEvent(tubeId, ref ev); @@ -342,5 +460,28 @@ 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? tags = default, DisposalEntryComponent? entry = null) + { + if (!Resolve(uid, ref entry)) + return false; + + var xform = Transform(uid); + var holder = Spawn(DisposalEntryComponent.HolderPrototypeId, xform.MapPosition); + var holderComponent = Comp(holder); + + foreach (var entity in from.Container.ContainedEntities.ToArray()) + { + _disposableSystem.TryInsert(holder, entity, holderComponent); + } + + _atmosSystem.Merge(holderComponent.Air, from.Air); + from.Air.Clear(); + + if (tags != default) + holderComponent.Tags.UnionWith(tags); + + return _disposableSystem.EnterTube(holder, uid, holderComponent); + } } } diff --git a/Content.Server/Disposal/Tube/GetDisposalsConnectableDirectionsEvent.cs b/Content.Server/Disposal/Tube/GetDisposalsConnectableDirectionsEvent.cs index c2a6fb7075..6139571cee 100644 --- a/Content.Server/Disposal/Tube/GetDisposalsConnectableDirectionsEvent.cs +++ b/Content.Server/Disposal/Tube/GetDisposalsConnectableDirectionsEvent.cs @@ -1,6 +1,4 @@ -using Content.Server.Disposal.Unit.Components; - -namespace Content.Server.Disposal.Tube; +namespace Content.Server.Disposal.Tube; [ByRefEvent] public record struct GetDisposalsConnectableDirectionsEvent diff --git a/Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs b/Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs index e6fe9998b2..653d7789b0 100644 --- a/Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs +++ b/Content.Server/Disposal/Unit/Components/DisposalHolderComponent.cs @@ -1,18 +1,12 @@ using Content.Server.Atmos; using Content.Server.Disposal.Tube.Components; -using Content.Shared.Body.Components; -using Content.Shared.Item; using Robust.Shared.Containers; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; namespace Content.Server.Disposal.Unit.Components { [RegisterComponent] public sealed class DisposalHolderComponent : Component, IGasMixtureHolder { - [Dependency] private readonly IEntityManager _entMan = default!; - public Container Container = null!; /// @@ -29,7 +23,7 @@ namespace Content.Server.Disposal.Unit.Components public float TimeLeft { get; set; } [ViewVariables] - public DisposalTubeComponent? PreviousTube { get; set; } + public EntityUid? PreviousTube { get; set; } [ViewVariables] public Direction PreviousDirection { get; set; } = Direction.Invalid; @@ -38,7 +32,7 @@ namespace Content.Server.Disposal.Unit.Components public Direction PreviousDirectionFrom => (PreviousDirection == Direction.Invalid) ? Direction.Invalid : PreviousDirection.GetOpposite(); [ViewVariables] - public DisposalTubeComponent? CurrentTube { get; set; } + public EntityUid? CurrentTube { get; set; } // CurrentDirection is not null when CurrentTube isn't null. [ViewVariables] @@ -55,39 +49,6 @@ namespace Content.Server.Disposal.Unit.Components public HashSet Tags { get; set; } = new(); [DataField("air")] - public GasMixture Air { get; set; } = new (70); - - protected override void Initialize() - { - base.Initialize(); - - Container = ContainerHelpers.EnsureContainer(Owner, nameof(DisposalHolderComponent)); - } - - private bool CanInsert(EntityUid entity) - { - if (!Container.CanInsert(entity)) - { - return false; - } - - return _entMan.HasComponent(entity) || - _entMan.HasComponent(entity); - } - - public bool TryInsert(EntityUid entity) - { - if (!CanInsert(entity) || !Container.Insert(entity)) - { - return false; - } - - if (_entMan.TryGetComponent(entity, out PhysicsComponent? physics)) - { - _entMan.System().SetCanCollide(entity, false, body: physics); - } - - return true; - } + public GasMixture Air { get; set; } = new(70); } } diff --git a/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs b/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs index 25e5ad708e..0cd7137362 100644 --- a/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs +++ b/Content.Server/Disposal/Unit/Components/DisposalUnitComponent.cs @@ -67,5 +67,11 @@ namespace Content.Server.Disposal.Unit.Components [DataField("air")] public GasMixture Air { get; set; } = new(Atmospherics.CellVolume); + + [ViewVariables] + public TimeSpan NextFlush = TimeSpan.MaxValue; + + [ViewVariables] + public bool AutoFlushing = false; } } diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs index 0f8d14670f..8f42c957f1 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs @@ -3,7 +3,10 @@ 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.Item; using JetBrains.Annotations; +using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; @@ -18,6 +21,50 @@ namespace Content.Server.Disposal.Unit.EntitySystems [Dependency] private readonly DisposalTubeSystem _disposalTubeSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentStartup); + } + + private void OnComponentStartup(EntityUid uid, DisposalHolderComponent holder, ComponentStartup args) + { + holder.Container = _containerSystem.EnsureContainer(uid, nameof(DisposalHolderComponent)); + } + + public bool TryInsert(EntityUid uid, EntityUid toInsert, DisposalHolderComponent? holder = null) + { + if (!Resolve(uid, ref holder)) + return false; + if (!CanInsert(uid, toInsert, holder)) + return false; + + if (!holder.Container.Insert(toInsert, EntityManager)) + return false; + + if (TryComp(toInsert, out var physBody)) + _physicsSystem.SetCanCollide(toInsert, false, body: physBody); + + return true; + } + + private bool CanInsert(EntityUid uid, EntityUid toInsert, DisposalHolderComponent? holder = null) + { + if (!Resolve(uid, ref holder)) + return false; + + if (!holder.Container.CanInsert(toInsert)) + { + return false; + } + + return HasComp(toInsert) || + HasComp(toInsert); + } public void ExitDisposals(EntityUid uid, DisposalHolderComponent? holder = null, TransformComponent? holderTransform = null) { @@ -28,7 +75,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems return; if (holder.IsExitingDisposals) { - Logger.ErrorS("c.s.disposal.holder", "Tried exiting disposals twice. This should never happen."); + Log.Error("Tried exiting disposals twice. This should never happen."); return; } holder.IsExitingDisposals = true; @@ -65,7 +112,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (duc != null) duc.Container.Insert(entity, EntityManager, xform, meta: meta); else - xform.AttachToGridOrMap(); + _xformSystem.AttachToGridOrMap(entity, xform); if (EntityManager.TryGetComponent(entity, out PhysicsComponent? physics)) { @@ -78,7 +125,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems _disposalUnitSystem.TryEjectContents(disposalId.Value, duc); } - if (_atmosphereSystem.GetContainingMixture(uid, false, true) is {} environment) + if (_atmosphereSystem.GetContainingMixture(uid, false, true) is { } environment) { _atmosphereSystem.Merge(environment, holder.Air); holder.Air.Clear(); @@ -94,7 +141,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems return false; if (holder.IsExitingDisposals) { - Logger.ErrorS("c.s.disposal.holder", "Tried entering tube after exiting disposals. This should never happen."); + Log.Error("Tried entering tube after exiting disposals. This should never happen."); return false; } if (!Resolve(toUid, ref to, ref toTransform)) @@ -106,11 +153,11 @@ namespace Content.Server.Disposal.Unit.EntitySystems foreach (var ent in holder.Container.ContainedEntities) { var comp = EnsureComp(ent); - comp.Holder = holder.Owner; + comp.Holder = holderUid; } // Insert into next tube - if (!to.Contents.Insert(holder.Owner)) + if (!to.Contents.Insert(holderUid)) { ExitDisposals(holderUid, holder, holderTransform); return false; @@ -121,7 +168,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems holder.PreviousTube = holder.CurrentTube; holder.PreviousDirection = holder.CurrentDirection; } - holder.CurrentTube = to; + holder.CurrentTube = toUid; var ev = new GetDisposalsNextDirectionEvent(holder); RaiseLocalEvent(toUid, ref ev); holder.CurrentDirection = ev.Next; @@ -140,13 +187,14 @@ namespace Content.Server.Disposal.Unit.EntitySystems public override void Update(float frameTime) { - foreach (var comp in EntityManager.EntityQuery()) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var holder)) { - UpdateComp(comp, frameTime); + UpdateComp(uid, holder, frameTime); } } - private void UpdateComp(DisposalHolderComponent holder, float frameTime) + private void UpdateComp(EntityUid uid, DisposalHolderComponent holder, float frameTime) { while (frameTime > 0) { @@ -159,40 +207,39 @@ namespace Content.Server.Disposal.Unit.EntitySystems holder.TimeLeft -= time; frameTime -= time; - var currentTube = holder.CurrentTube; - if (currentTube == null || currentTube.Deleted) + if (!EntityManager.EntityExists(holder.CurrentTube)) { - ExitDisposals((holder).Owner); + ExitDisposals(uid, holder); break; } + var currentTube = holder.CurrentTube!.Value; if (holder.TimeLeft > 0) { var progress = 1 - holder.TimeLeft / holder.StartingTime; - var origin = EntityManager.GetComponent(currentTube.Owner).Coordinates; + var origin = Transform(currentTube).Coordinates; var destination = holder.CurrentDirection.ToVec(); var newPosition = destination * progress; // This is some supreme shit code. - EntityManager.GetComponent(holder.Owner).Coordinates = origin.Offset(newPosition).WithEntityId(currentTube.Owner); - + _xformSystem.SetCoordinates(uid, origin.Offset(newPosition).WithEntityId(currentTube)); continue; } // Past this point, we are performing inter-tube transfer! // Remove current tube content - currentTube.Contents.Remove(holder.Owner, reparent: false, force: true); + Comp(currentTube).Contents.Remove(uid, reparent: false, force: true); // Find next tube - var nextTube = _disposalTubeSystem.NextTubeFor(currentTube.Owner, holder.CurrentDirection); - if (nextTube == null || nextTube.Deleted) + var nextTube = _disposalTubeSystem.NextTubeFor(currentTube, holder.CurrentDirection); + if (!EntityManager.EntityExists(nextTube)) { - ExitDisposals((holder).Owner); + ExitDisposals(uid, holder); break; } // Perform remainder of entry process - if (!EnterTube((holder).Owner, nextTube.Owner, holder)) + if (!EnterTube(uid, nextTube!.Value, holder)) { break; } diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 49aa7313a3..1deb292d70 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading; using Content.Server.Administration.Logs; using Content.Server.Atmos.EntitySystems; +using Content.Server.Disposal.Tube; using Content.Server.Disposal.Tube.Components; using Content.Server.Disposal.Unit.Components; using Content.Server.Popups; @@ -29,6 +30,7 @@ using Robust.Shared.Containers; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Random; +using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Disposal.Unit.EntitySystems @@ -48,6 +50,9 @@ namespace Content.Server.Disposal.Unit.EntitySystems [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly PowerReceiverSystem _power = default!; + [Dependency] private readonly DisposalTubeSystem _disposalTubeSystem = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + public override void Initialize() { @@ -92,11 +97,13 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (component.Container.ContainedEntities.Count > 0) { // Verbs to flush the unit - AlternativeVerb flushVerb = new(); - flushVerb.Act = () => Engage(uid, component); - flushVerb.Text = Loc.GetString("disposal-flush-verb-get-data-text"); - flushVerb.Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png")); - flushVerb.Priority = 1; + AlternativeVerb flushVerb = new() + { + Act = () => Engage(uid, component), + Text = Loc.GetString("disposal-flush-verb-get-data-text"), + Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png")), + Priority = 1, + }; args.Verbs.Add(flushVerb); // Verb to eject the contents @@ -143,7 +150,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (!_actionBlockerSystem.CanDrop(args.User)) return; - if (!CanInsert(component, args.Using.Value)) + if (!CanInsert(uid, component, args.Using.Value)) return; InteractionVerb insertVerb = new() @@ -186,10 +193,11 @@ namespace Content.Server.Disposal.Unit.EntitySystems public override void Update(float frameTime) { base.Update(frameTime); - foreach (var (_, comp) in EntityQuery()) + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var _, out var unit)) { - var uid = comp.Owner; - if (!Update(uid, comp, frameTime)) + if (!Update(uid, unit, frameTime)) continue; RemComp(uid); @@ -199,7 +207,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems #region UI Handlers private void OnUiButtonPressed(EntityUid uid, DisposalUnitComponent component, SharedDisposalUnitComponent.UiButtonPressedMessage args) { - if (args.Session.AttachedEntity is not {Valid: true} player) + if (args.Session.AttachedEntity is not { Valid: true } player) { return; } @@ -218,7 +226,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems _power.TogglePower(uid, user: args.Session.AttachedEntity); break; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException($"{ToPrettyString(player):player} attempted to hit a nonexistant button on {ToPrettyString(uid)}"); } } @@ -259,7 +267,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems return; } - if (!CanInsert(component, args.Used) || !_handsSystem.TryDropIntoContainer(args.User, args.Used, component.Container)) + if (!CanInsert(uid, component, args.Used) || !_handsSystem.TryDropIntoContainer(args.User, args.Used, component.Container)) { return; } @@ -274,7 +282,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems /// private void HandleThrowCollide(EntityUid uid, DisposalUnitComponent component, ThrowHitByEvent args) { - if (!CanInsert(component, args.Thrown) || + if (!CanInsert(uid, component, args.Thrown) || _robustRandom.NextDouble() > 0.75 || !component.Container.Insert(args.Thrown)) { @@ -295,7 +303,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (!HasComp(uid)) { - Logger.WarningS("VitalComponentMissing", $"Disposal unit {uid} is missing an {nameof(AnchorableComponent)}"); + Log.Warning($"Disposal unit {uid} is missing an {nameof(AnchorableComponent)}"); } } @@ -307,8 +315,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems } _ui.TryCloseAll(uid, SharedDisposalUnitComponent.DisposalUnitUiKey.Key); - component.AutomaticEngageToken?.Cancel(); - component.AutomaticEngageToken = null; + component.NextFlush = TimeSpan.MaxValue; component.Container = null!; RemComp(uid); @@ -324,11 +331,10 @@ namespace Content.Server.Disposal.Unit.EntitySystems // TODO: Need to check the other stuff. if (!args.Powered) { - component.AutomaticEngageToken?.Cancel(); - component.AutomaticEngageToken = null; + component.NextFlush = TimeSpan.MaxValue; } - HandleStateChange(uid, component, args.Powered && component.State == SharedDisposalUnitComponent.PressureState.Pressurizing); + HandleStateChange(uid, component, args.Powered && (component.State == SharedDisposalUnitComponent.PressureState.Pressurizing || component.NextFlush != TimeSpan.MaxValue)); UpdateVisualState(uid, component); UpdateInterface(uid, component, args.Powered); @@ -341,7 +347,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems /// /// Add or remove this disposal from the active ones for updating. /// - public void HandleStateChange(EntityUid uid, DisposalUnitComponent component, bool active) + public void HandleStateChange(EntityUid uid, DisposalUnitComponent _, bool active) { if (active) { @@ -416,20 +422,27 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (component.State == SharedDisposalUnitComponent.PressureState.Pressurizing) { var oldTimeElapsed = oldPressure / PressurePerSecond; - if (oldTimeElapsed < component.FlushTime && (oldTimeElapsed + frameTime) >= component.FlushTime) + if (oldTimeElapsed < component.FlushTime && oldTimeElapsed + frameTime >= component.FlushTime) { // We've crossed over the amount of time it takes to flush. This will switch the // visuals over to a 'Charging' state. UpdateVisualState(uid, component); } } + else if (component.State == SharedDisposalUnitComponent.PressureState.Ready && component.NextFlush < _gameTiming.CurTime) + { + if (!TryFlush(uid, component) && component.AutoFlushing) + TryQueueEngage(uid, component); + else + component.AutoFlushing = false; + } Box2? disposalsBounds = null; var count = component.RecentlyEjected.Count; if (count > 0) { - if (!TryComp(uid, out PhysicsComponent? disposalsBody)) + if (!HasComp(uid)) { component.RecentlyEjected.Clear(); } @@ -442,8 +455,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems for (var i = component.RecentlyEjected.Count - 1; i >= 0; i--) { var ejectedId = component.RecentlyEjected[i]; - if (Exists(ejectedId) && - TryComp(ejectedId, out PhysicsComponent? body)) + if (HasComp(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 :^) @@ -460,7 +472,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (count != component.RecentlyEjected.Count) Dirty(component); - return state == SharedDisposalUnitComponent.PressureState.Ready && component.RecentlyEjected.Count == 0; + return state == SharedDisposalUnitComponent.PressureState.Ready && component.NextFlush == TimeSpan.MaxValue && component.RecentlyEjected.Count == 0; } public bool TryInsert(EntityUid unitId, EntityUid toInsertId, EntityUid? userId, DisposalUnitComponent? unit = null) @@ -474,7 +486,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems return false; } - if (!CanInsert(unit, toInsertId)) + if (!CanInsert(unitId, unit, toInsertId)) return false; var delay = userId == toInsertId ? unit.EntryDelay : unit.DraggedEntryDelay; @@ -507,6 +519,8 @@ namespace Content.Server.Disposal.Unit.EntitySystems return false; } + component.NextFlush = TimeSpan.MaxValue; + //Allows the MailingUnitSystem to add tags or prevent flushing var beforeFlushArgs = new BeforeDisposalFlushEvent(); RaiseLocalEvent(uid, beforeFlushArgs); @@ -534,17 +548,16 @@ namespace Content.Server.Disposal.Unit.EntitySystems var entryComponent = Comp(entry); var indices = _transformSystem.GetGridOrMapTilePosition(uid, xform); - if (_atmosSystem.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is {Temperature: > 0} environment) + 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); } - entryComponent.TryInsert(component, beforeFlushArgs.Tags); + _disposalTubeSystem.TryInsert(entry, component, beforeFlushArgs.Tags); - component.AutomaticEngageToken?.Cancel(); - component.AutomaticEngageToken = null; + component.NextFlush = TimeSpan.MaxValue; if (!component.DisablePressure) { @@ -642,8 +655,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (component.Container.ContainedEntities.Count == 0) { - component.AutomaticEngageToken?.Cancel(); - component.AutomaticEngageToken = null; + component.NextFlush = TimeSpan.MaxValue; } if (!component.RecentlyEjected.Contains(toRemove)) @@ -669,7 +681,8 @@ namespace Content.Server.Disposal.Unit.EntitySystems if (CanFlush(uid, component)) { - uid.SpawnTimer(component.FlushDelay, () => TryFlush(uid, component)); + component.NextFlush = _gameTiming.CurTime + component.FlushDelay; + EnsureComp(uid); } } @@ -691,9 +704,9 @@ namespace Content.Server.Disposal.Unit.EntitySystems } } - public override bool CanInsert(SharedDisposalUnitComponent component, EntityUid entity) + public override bool CanInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid entity) { - if (!base.CanInsert(component, entity) || component is not DisposalUnitComponent serverComp) + if (!base.CanInsert(uid, component, entity) || component is not DisposalUnitComponent serverComp) return false; return serverComp.Container.CanInsert(entity); @@ -709,15 +722,10 @@ namespace Content.Server.Disposal.Unit.EntitySystems return; } - component.AutomaticEngageToken = new CancellationTokenSource(); + component.NextFlush = _gameTiming.CurTime + component.AutomaticEngageTime; + component.AutoFlushing = true; - uid.SpawnTimer(component.AutomaticEngageTime, () => - { - if (!TryFlush(uid, component)) - { - TryQueueEngage(uid, component); - } - }, component.AutomaticEngageToken.Token); + EnsureComp(uid); } public void AfterInsert(EntityUid uid, DisposalUnitComponent component, EntityUid inserted, EntityUid? user = null) diff --git a/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs b/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs index 4f67af67a4..65be092072 100644 --- a/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs +++ b/Content.Shared/Disposal/MailingUnitBoundUserInterfaceState.cs @@ -4,7 +4,7 @@ using Robust.Shared.Serialization; namespace Content.Shared.Disposal; [Serializable, NetSerializable] -public sealed class MailingUnitBoundUserInterfaceState: BoundUserInterfaceState, IEquatable +public sealed class MailingUnitBoundUserInterfaceState : BoundUserInterfaceState, IEquatable { public string? Target; public List TargetList; @@ -21,11 +21,25 @@ public sealed class MailingUnitBoundUserInterfaceState: BoundUserInterfaceState, public bool Equals(MailingUnitBoundUserInterfaceState? other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + 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/MailingUnitUiMessages.cs b/Content.Shared/Disposal/MailingUnitUiMessages.cs index 42c3a521a9..0e5047e362 100644 --- a/Content.Shared/Disposal/MailingUnitUiMessages.cs +++ b/Content.Shared/Disposal/MailingUnitUiMessages.cs @@ -14,10 +14,10 @@ public enum MailingUnitUiKey : byte [Serializable, NetSerializable] public sealed class TargetSelectedMessage : BoundUserInterfaceMessage { - public readonly string? target; + public readonly string? Target; public TargetSelectedMessage(string? target) { - this.target = target; + Target = target; } } diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs index cc573c4243..5e80a85d7c 100644 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs @@ -63,7 +63,7 @@ namespace Content.Shared.Disposal if (args.Handled) return; - args.CanDrop = CanInsert(component, args.Dragged); + args.CanDrop = CanInsert(uid, component, args.Dragged); args.Handled = true; } @@ -73,9 +73,9 @@ namespace Content.Shared.Disposal args.Handled = true; } - public virtual bool CanInsert(SharedDisposalUnitComponent component, EntityUid entity) + public virtual bool CanInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid entity) { - if (!EntityManager.GetComponent(component.Owner).Anchored) + if (!EntityManager.GetComponent(uid).Anchored) return false; // TODO: Probably just need a disposable tag.