Fix ghosts getting spawned in nullspace (#27617)

* Add tests for ghost spawn position

* Make ghosts spawn immediately

* Format mind system

* Move ghost spawning to GhostSystem

* Spawn ghost on grid or map

This fixes the ghosts being attached the parent entity instead of the grid.

* Move logging out of the ghost system

* Make round start observer spawn using GhostSystem

* Move GameTicker ghost spawning to GhostSystem

Moved the more robust character name selection code over.
Moved the TimeOfDeath code over.
Added canReturn logic.

* Add overrides and default for ghost spawn coordinates

* Add warning log to ghost spawn fail

* Clean up test

* Dont spawn ghost on map delete

* Minor changes to the role test

* Fix role test failing to spawn ghost

It was failing the map check due to using Nullspace

* Fix ghost tests when running in parallel

Not sure what happened, but it seems to be because they were running simultaneously and overwriting values.

* Clean up ghost tests

* Test that map deletion does not spawn ghosts

* Spawn ghost on the next available map

* Disallow spawning on deleted maps

* Fix map deletion ghost test

* Cleanup
This commit is contained in:
ShadowCommander
2024-05-11 08:03:40 -07:00
committed by GitHub
parent 742a1a5fbd
commit a985c5e83e
7 changed files with 261 additions and 99 deletions

View File

@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Administration.Logs;
using Content.Server.GameTicking;
using Content.Server.Ghost;
using Content.Server.Mind.Commands;
using Content.Shared.Database;
using Content.Shared.Ghost;
@@ -9,10 +10,8 @@ using Content.Shared.Mind.Components;
using Content.Shared.Players;
using Robust.Server.GameStates;
using Robust.Server.Player;
using Robust.Shared.Map.Components;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Mind;
@@ -22,8 +21,7 @@ public sealed class MindSystem : SharedMindSystem
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedGhostSystem _ghosts = default!;
[Dependency] private readonly GhostSystem _ghosts = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly PvsOverrideSystem _pvsOverride = default!;
@@ -63,8 +61,8 @@ public sealed class MindSystem : SharedMindSystem
&& !Terminating(visiting))
{
TransferTo(mindId, visiting, mind: mind);
if (TryComp(visiting, out GhostComponent? ghost))
_ghosts.SetCanReturnToBody(ghost, false);
if (TryComp(visiting, out GhostComponent? ghostComp))
_ghosts.SetCanReturnToBody(ghostComp, false);
return;
}
@@ -74,40 +72,13 @@ public sealed class MindSystem : SharedMindSystem
if (!component.GhostOnShutdown || mind.Session == null || _gameTicker.RunLevel == GameRunLevel.PreRoundLobby)
return;
var xform = Transform(uid);
var gridId = xform.GridUid;
var spawnPosition = Transform(uid).Coordinates;
// Use a regular timer here because the entity has probably been deleted.
Timer.Spawn(0, () =>
{
// Make extra sure the round didn't end between spawning the timer and it being executed.
if (_gameTicker.RunLevel == GameRunLevel.PreRoundLobby)
return;
// Async this so that we don't throw if the grid we're on is being deleted.
if (!HasComp<MapGridComponent>(gridId))
spawnPosition = _gameTicker.GetObserverSpawnPoint();
// TODO refactor observer spawning.
// please.
if (!spawnPosition.IsValid(EntityManager))
{
// This should be an error, if it didn't cause tests to start erroring when they delete a player.
Log.Warning($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, and no applicable spawn location is available.");
TransferTo(mindId, null, createGhost: false, mind: mind);
return;
}
var ghost = Spawn(GameTicker.ObserverPrototypeName, spawnPosition);
var ghostComponent = Comp<GhostComponent>(ghost);
_ghosts.SetCanReturnToBody(ghostComponent, false);
var ghost = _ghosts.SpawnGhost((mindId, mind), uid);
if (ghost != null)
// Log these to make sure they're not causing the GameTicker round restart bugs...
Log.Debug($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, spawned \"{ToPrettyString(ghost)}\".");
_metaData.SetEntityName(ghost, mind.CharacterName ?? string.Empty);
TransferTo(mindId, ghost, mind: mind);
});
else
// This should be an error, if it didn't cause tests to start erroring when they delete a player.
Log.Warning($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, and no applicable spawn location is available.");
}
public override bool TryGetMind(NetUserId user, [NotNullWhen(true)] out EntityUid? mindId, [NotNullWhen(true)] out MindComponent? mind)