From 712fbcce42085db970bf157dabcb2dcbb627e7f8 Mon Sep 17 00:00:00 2001 From: Kronyxxx Date: Tue, 5 May 2026 23:59:41 +0000 Subject: [PATCH 1/3] fix(colcomm): protect colcomm grid and shuttle from trash cleanup --- .../Systems/EmergencyShuttleSystem.cs | 38 +++++++++++++++++++ Resources/Maps/colcomm.yml | 1 + 2 files changed, 39 insertions(+) diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index 8ad6ad43c56..18056273e89 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -8,6 +8,7 @@ using Content.Server.Communications; using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking.Events; +using Content.Server._HL.Cleanup; using Content.Server.Pinpointer; using Content.Server.Popups; using Content.Server.RoundEnd; @@ -81,6 +82,8 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem private EntityUid? _singletonColcommMap; private EntityUid? _singletonColcommGrid; private EntityUid? _singletonColcommShuttle; + private TimeSpan _nextColcommValidation; + private static readonly TimeSpan ColcommValidationInterval = TimeSpan.FromSeconds(30); public override void Initialize() { @@ -165,6 +168,38 @@ public override void Update(float frameTime) { base.Update(frameTime); UpdateEmergencyConsole(frameTime); + ValidateColcommState(); + } + + private void ValidateColcommState() + { + if (_timing.CurTime < _nextColcommValidation) + return; + + _nextColcommValidation = _timing.CurTime + ColcommValidationInterval; + + var query = AllEntityQuery(); + while (query.MoveNext(out var stationUid, out var colcomm)) + { + if (colcomm.Entity != null && Exists(colcomm.Entity.Value)) + { + EnsureComp(colcomm.Entity.Value); + + if (TryComp(colcomm.Entity.Value, out TransformComponent? xform) && xform.MapUid != null) + colcomm.MapEntity = xform.MapUid; + + continue; + } + + colcomm.Entity = null; + if (colcomm.MapEntity != null && !Exists(colcomm.MapEntity.Value)) + colcomm.MapEntity = null; + + AddColcomm(stationUid, colcomm); + + if (TryComp(stationUid, out var emergency)) + AddEmergencyShuttle((stationUid, emergency, colcomm)); + } } /// @@ -516,6 +551,7 @@ private void AddColcomm(EntityUid station, StationColcommComponent component) { component.MapEntity = _singletonColcommMap; component.Entity = _singletonColcommGrid; + EnsureComp(_singletonColcommGrid.Value); return; } @@ -544,6 +580,7 @@ private void AddColcomm(EntityUid station, StationColcommComponent component) component.Entity = grid; _singletonColcommMap = map; _singletonColcommGrid = grid; + EnsureComp(grid.Value); _metaData.SetEntityName(map, Loc.GetString("map-name-Colcomm")); _shuttle.TryAddFTLDestination(mapId, true, out _); Log.Info($"Created Colcomm grid {ToPrettyString(grid)} on map {ToPrettyString(map)} for station {ToPrettyString(station)}"); @@ -618,6 +655,7 @@ private void AddEmergencyShuttle(Entity(shuttle.Value); + EnsureComp(shuttle.Value); EnsureComp(shuttle.Value); EnsureComp(shuttle.Value); diff --git a/Resources/Maps/colcomm.yml b/Resources/Maps/colcomm.yml index dedb4f116d0..6eb81d70ced 100644 --- a/Resources/Maps/colcomm.yml +++ b/Resources/Maps/colcomm.yml @@ -515,6 +515,7 @@ entities: - type: GridPathfinding - type: BecomesStation id: Colcomm + - type: CleanupProtectedGrid - type: Gravity gravityShakeSound: !type:SoundPathSpecifier path: /Audio/Effects/alert.ogg From c3363da5ded49943e664fe86715f6b24ea4c844f Mon Sep 17 00:00:00 2001 From: Kronyxxx Date: Wed, 6 May 2026 00:04:49 +0000 Subject: [PATCH 2/3] fix(colcomm): restore via normal spawn pipeline when missing --- .../Systems/EmergencyShuttleSystem.cs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index 18056273e89..7a873b2fb5d 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -178,8 +178,12 @@ private void ValidateColcommState() _nextColcommValidation = _timing.CurTime + ColcommValidationInterval; + var singletonValid = _singletonColcommMap != null && _singletonColcommGrid != null + && Exists(_singletonColcommMap.Value) && Exists(_singletonColcommGrid.Value); + var needsRespawn = false; + var query = AllEntityQuery(); - while (query.MoveNext(out var stationUid, out var colcomm)) + while (query.MoveNext(out var _, out var colcomm)) { if (colcomm.Entity != null && Exists(colcomm.Entity.Value)) { @@ -188,18 +192,48 @@ private void ValidateColcommState() if (TryComp(colcomm.Entity.Value, out TransformComponent? xform) && xform.MapUid != null) colcomm.MapEntity = xform.MapUid; + if (!singletonValid) + { + _singletonColcommGrid = colcomm.Entity; + _singletonColcommMap = colcomm.MapEntity; + singletonValid = _singletonColcommMap != null && _singletonColcommGrid != null + && Exists(_singletonColcommMap.Value) && Exists(_singletonColcommGrid.Value); + } + + continue; + } + + if (singletonValid) + { + colcomm.Entity = _singletonColcommGrid; + colcomm.MapEntity = _singletonColcommMap; continue; } colcomm.Entity = null; - if (colcomm.MapEntity != null && !Exists(colcomm.MapEntity.Value)) - colcomm.MapEntity = null; + colcomm.MapEntity = null; + needsRespawn = true; + } - AddColcomm(stationUid, colcomm); + if (needsRespawn) + RespawnColcommViaSpawnPath(); + } + + private void RespawnColcommViaSpawnPath() + { + _singletonColcommMap = null; + _singletonColcommGrid = null; + _singletonColcommShuttle = null; - if (TryComp(stationUid, out var emergency)) - AddEmergencyShuttle((stationUid, emergency, colcomm)); + var query = AllEntityQuery(); + while (query.MoveNext(out var _, out var colcomm)) + { + colcomm.Entity = null; + colcomm.MapEntity = null; } + + // Reuse the normal startup flow to restore ColComm + shuttle consistently. + SetupEmergencyShuttle(); } /// From 83d5ef52a0bad0ed448ba395ae577350b07ffe4c Mon Sep 17 00:00:00 2001 From: Kronyxxx Date: Wed, 6 May 2026 00:22:32 +0000 Subject: [PATCH 3/3] fix(trash-cleanup): protect grids hosting worldloaders --- Content.Server/Trash/TrashCleanupSystem.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Content.Server/Trash/TrashCleanupSystem.cs b/Content.Server/Trash/TrashCleanupSystem.cs index 77aacf94452..558820e46d2 100644 --- a/Content.Server/Trash/TrashCleanupSystem.cs +++ b/Content.Server/Trash/TrashCleanupSystem.cs @@ -208,6 +208,10 @@ private bool ShouldProtectGrid(EntityUid gridUid) if (HasComp(gridUid)) return true; + // A grid with a world loader is an active world-loading anchor and should never be trash-cleaned. + if (HasComp(gridUid)) + return true; + // Protect grids with important components that indicate they shouldn't be deleted if (HasComp(gridUid) || HasComp(gridUid)) return true;