diff --git a/Content.IntegrationTests/Tests/Minds/GhostTests.cs b/Content.IntegrationTests/Tests/Minds/GhostTests.cs index ad9d53a70d..7a156e71e4 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostTests.cs @@ -156,4 +156,20 @@ public sealed class GhostTests await data.Pair.CleanReturnAsync(); } + [Test] + public async Task TestGhostGridNotTerminating() + { + var data = await SetupData(); + + Assert.DoesNotThrowAsync(async () => + { + // Delete the grid + await data.Server.WaitPost(() => data.SEntMan.DeleteEntity(data.MapData.Grid.Owner)); + }); + + await data.Pair.RunTicksSync(5); + + await data.Pair.CleanReturnAsync(); + } + } diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 7478560379..843122e0fe 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -369,11 +369,16 @@ namespace Content.Server.GameTicking public EntityCoordinates GetObserverSpawnPoint() { _possiblePositions.Clear(); - - foreach (var (point, transform) in EntityManager.EntityQuery(true)) + var spawnPointQuery = EntityManager.EntityQueryEnumerator(); + while (spawnPointQuery.MoveNext(out var uid, out var point, out var transform)) { - if (point.SpawnType != SpawnPointType.Observer) + if (point.SpawnType != SpawnPointType.Observer + || TerminatingOrDeleted(uid) + || transform.MapUid == null + || TerminatingOrDeleted(transform.MapUid.Value)) + { continue; + } _possiblePositions.Add(transform.Coordinates); } @@ -415,7 +420,9 @@ namespace Content.Server.GameTicking if (_mapManager.MapExists(DefaultMap)) { - return new EntityCoordinates(_mapManager.GetMapEntityId(DefaultMap), Vector2.Zero); + var mapUid = _mapManager.GetMapEntityId(DefaultMap); + if (!TerminatingOrDeleted(mapUid)) + return new EntityCoordinates(mapUid, Vector2.Zero); } // Just pick a point at this point I guess. diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index f4e6a4d607..6c3d69fea7 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -409,23 +409,41 @@ namespace Content.Server.Ghost return SpawnGhost(mind, spawnPosition, canReturn); } + private bool IsValidSpawnPosition(EntityCoordinates? spawnPosition) + { + if (spawnPosition?.IsValid(EntityManager) != true) + return false; + + var mapUid = spawnPosition?.GetMapUid(EntityManager); + var gridUid = spawnPosition?.EntityId; + // Test if the map is being deleted + if (mapUid == null || TerminatingOrDeleted(mapUid.Value)) + return false; + // Test if the grid is being deleted + if (gridUid != null && TerminatingOrDeleted(gridUid.Value)) + return false; + + return true; + } + public EntityUid? SpawnGhost(Entity mind, EntityCoordinates? spawnPosition = null, bool canReturn = false) { if (!Resolve(mind, ref mind.Comp)) return null; - // Test if the map is being deleted - var mapUid = spawnPosition?.GetMapUid(EntityManager); - if (mapUid == null || TerminatingOrDeleted(mapUid.Value)) + // Test if the map or grid is being deleted + if (!IsValidSpawnPosition(spawnPosition)) spawnPosition = null; + // If it's bad, look for a valid point to spawn spawnPosition ??= _ticker.GetObserverSpawnPoint(); - if (!spawnPosition.Value.IsValid(EntityManager)) + // Make sure the new point is valid too + if (!IsValidSpawnPosition(spawnPosition)) { Log.Warning($"No spawn valid ghost spawn position found for {mind.Comp.CharacterName}" - + " \"{ToPrettyString(mind)}\""); + + $" \"{ToPrettyString(mind)}\""); _minds.TransferTo(mind.Owner, null, createGhost: false, mind: mind.Comp); return null; }