]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add NukeOps Test (#27207)
authorLeon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Wed, 24 Apr 2024 05:38:43 +0000 (17:38 +1200)
committerGitHub <noreply@github.com>
Wed, 24 Apr 2024 05:38:43 +0000 (15:38 +1000)
* Add NukeOps Test

* Update EvacShuttleTest to also check mapinit

* Update RuleMaxTimeRestartTest

* Fix cvar cleanup

* A

* Revert some changes

* comments

* Add MappingTests

* Finally fix the test

* A

16 files changed:
Content.IntegrationTests/Pair/TestPair.Timing.cs
Content.IntegrationTests/PoolManager.Cvars.cs
Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs [new file with mode: 0644]
Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs
Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs
Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
Content.IntegrationTests/Tests/Mapping/MappingTests.cs [new file with mode: 0644]
Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs
Content.Server/CriminalRecords/Systems/CriminalRecordsConsoleSystem.cs
Content.Server/GameTicking/GameTicker.GamePreset.cs
Content.Server/GameTicking/GameTicker.RoundFlow.cs
Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs
Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs
Content.Server/Mapping/MappingCommand.cs
Content.Server/StationRecords/Systems/StationRecordsSystem.cs
Content.Shared/Roles/SharedRoleSystem.cs

index 3487ea6801035c7451bd2790a235fb66bb363197..e0859660d42d8e5b30b575a521b38e239db2d2c2 100644 (file)
@@ -1,5 +1,4 @@
 #nullable enable
-using Robust.Shared.Timing;
 
 namespace Content.IntegrationTests.Pair;
 
@@ -19,6 +18,22 @@ public sealed partial class TestPair
         }
     }
 
+    /// <summary>
+    /// Convert a time interval to some number of ticks.
+    /// </summary>
+    public int SecondsToTicks(float seconds)
+    {
+        return (int) Math.Ceiling(seconds / Server.Timing.TickPeriod.TotalSeconds);
+    }
+
+    /// <summary>
+    /// Run the server & client in sync for some amount of time
+    /// </summary>
+    public async Task RunSeconds(float seconds)
+    {
+        await RunTicksSync(SecondsToTicks(seconds));
+    }
+
     /// <summary>
     /// Runs the server-client pair in sync, but also ensures they are both idle each tick.
     /// </summary>
@@ -59,4 +74,4 @@ public sealed partial class TestPair
         delta = cTick - sTick;
         Assert.That(delta, Is.EqualTo(targetDelta));
     }
-}
\ No newline at end of file
+}
index 327ec627f5209598af34318f9621bdf3ad165865..d39c7284d062c768305b72596974b8490818827e 100644 (file)
@@ -32,6 +32,7 @@ public static partial class PoolManager
         (CCVars.GameLobbyEnabled.Name, "false"),
         (CCVars.ConfigPresetDevelopment.Name, "false"),
         (CCVars.AdminLogsEnabled.Name, "false"),
+        (CCVars.AutosaveEnabled.Name, "false"),
         (CVars.NetBufferSize.Name, "0")
     };
 
diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs
new file mode 100644 (file)
index 0000000..5833db0
--- /dev/null
@@ -0,0 +1,187 @@
+#nullable enable
+using System.Linq;
+using Content.Server.Body.Components;
+using Content.Server.GameTicking;
+using Content.Server.GameTicking.Presets;
+using Content.Server.GameTicking.Rules.Components;
+using Content.Server.Mind;
+using Content.Server.Pinpointer;
+using Content.Server.Roles;
+using Content.Server.Shuttles.Components;
+using Content.Server.Station.Components;
+using Content.Shared.CCVar;
+using Content.Shared.Damage;
+using Content.Shared.FixedPoint;
+using Content.Shared.GameTicking;
+using Content.Shared.Hands.Components;
+using Content.Shared.Inventory;
+using Content.Shared.NPC.Systems;
+using Content.Shared.NukeOps;
+using Robust.Server.GameObjects;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map.Components;
+
+namespace Content.IntegrationTests.Tests.GameRules;
+
+[TestFixture]
+public sealed class NukeOpsTest
+{
+    /// <summary>
+    /// Check that a nuke ops game mode can start without issue. I.e., that the nuke station and such all get loaded.
+    /// </summary>
+    [Test]
+    public async Task TryStopNukeOpsFromConstantlyFailing()
+    {
+        await using var pair = await PoolManager.GetServerClient(new PoolSettings
+        {
+            Dirty = true,
+            DummyTicker = false,
+            Connected = true,
+            InLobby = true
+        });
+
+        var server = pair.Server;
+        var client = pair.Client;
+        var entMan = server.EntMan;
+        var mapSys = server.System<MapSystem>();
+        var ticker = server.System<GameTicker>();
+        var mindSys = server.System<MindSystem>();
+        var roleSys = server.System<RoleSystem>();
+        var invSys = server.System<InventorySystem>();
+        var factionSys = server.System<NpcFactionSystem>();
+
+        Assert.That(server.CfgMan.GetCVar(CCVars.GridFill), Is.False);
+        server.CfgMan.SetCVar(CCVars.GridFill, true);
+
+        // Initially in the lobby
+        Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby));
+        Assert.That(client.AttachedEntity, Is.Null);
+        Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay));
+
+        // There are no grids or maps
+        Assert.That(entMan.Count<MapComponent>(), Is.Zero);
+        Assert.That(entMan.Count<MapGridComponent>(), Is.Zero);
+        Assert.That(entMan.Count<StationMapComponent>(), Is.Zero);
+        Assert.That(entMan.Count<StationMemberComponent>(), Is.Zero);
+        Assert.That(entMan.Count<StationCentcommComponent>(), Is.Zero);
+
+        // And no nukie related components
+        Assert.That(entMan.Count<NukeopsRuleComponent>(), Is.Zero);
+        Assert.That(entMan.Count<NukeopsRoleComponent>(), Is.Zero);
+        Assert.That(entMan.Count<NukeOperativeComponent>(), Is.Zero);
+        Assert.That(entMan.Count<NukeOpsShuttleComponent>(), Is.Zero);
+        Assert.That(entMan.Count<NukeOperativeSpawnerComponent>(), Is.Zero);
+
+        // Ready up and start nukeops
+        await pair.WaitClientCommand("toggleready True");
+        Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.ReadyToPlay));
+        await pair.WaitCommand("forcepreset Nukeops");
+        await pair.RunTicksSync(10);
+
+        // Game should have started
+        Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound));
+        Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.JoinedGame));
+        Assert.That(client.EntMan.EntityExists(client.AttachedEntity));
+        var player = pair.Player!.AttachedEntity!.Value;
+        Assert.That(entMan.EntityExists(player));
+
+        // Maps now exist
+        Assert.That(entMan.Count<MapComponent>(), Is.GreaterThan(0));
+        Assert.That(entMan.Count<MapGridComponent>(), Is.GreaterThan(0));
+        Assert.That(entMan.Count<StationDataComponent>(), Is.EqualTo(2)); // The main station & nukie station
+        Assert.That(entMan.Count<StationMemberComponent>(), Is.GreaterThan(3)); // Each station has at least 1 grid, plus some shuttles
+        Assert.That(entMan.Count<StationCentcommComponent>(), Is.EqualTo(1));
+
+        // And we now have nukie related components
+        Assert.That(entMan.Count<NukeopsRuleComponent>(), Is.EqualTo(1));
+        Assert.That(entMan.Count<NukeopsRoleComponent>(), Is.EqualTo(1));
+        Assert.That(entMan.Count<NukeOperativeComponent>(), Is.EqualTo(1));
+        Assert.That(entMan.Count<NukeOpsShuttleComponent>(), Is.EqualTo(1));
+
+        // The player entity should be the nukie commander
+        var mind = mindSys.GetMind(player)!.Value;
+        Assert.That(entMan.HasComponent<NukeOperativeComponent>(player));
+        Assert.That(roleSys.MindIsAntagonist(mind));
+        Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mind));
+        Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True);
+        Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False);
+
+        var roles = roleSys.MindGetAllRoles(mind);
+        var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander" && x.Component is NukeopsRoleComponent);
+        Assert.That(cmdRoles.Count(), Is.EqualTo(1));
+
+        // The game rule exists, and all the stations/shuttles/maps are properly initialized
+        var rule = entMan.AllComponents<NukeopsRuleComponent>().Single().Component;
+        Assert.That(entMan.EntityExists(rule.NukieOutpost));
+        Assert.That(entMan.EntityExists(rule.NukieShuttle));
+        Assert.That(entMan.EntityExists(rule.TargetStation));
+
+        Assert.That(entMan.HasComponent<MapGridComponent>(rule.NukieOutpost));
+        Assert.That(entMan.HasComponent<MapGridComponent>(rule.NukieShuttle));
+
+        Assert.That(entMan.HasComponent<StationMemberComponent>(rule.NukieOutpost));
+        Assert.That(entMan.HasComponent<StationDataComponent>(rule.TargetStation));
+
+        var nukieStation = entMan.GetComponent<StationMemberComponent>(rule.NukieOutpost!.Value);
+        Assert.That(entMan.EntityExists(nukieStation.Station));
+        Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation));
+
+        Assert.That(server.MapMan.MapExists(rule.NukiePlanet));
+        var nukieMap = mapSys.GetMap(rule.NukiePlanet!.Value);
+
+        var targetStation = entMan.GetComponent<StationDataComponent>(rule.TargetStation!.Value);
+        var targetGrid = targetStation.Grids.First();
+        var targetMap = entMan.GetComponent<TransformComponent>(targetGrid).MapUid!.Value;
+        Assert.That(targetMap, Is.Not.EqualTo(nukieMap));
+
+        Assert.That(entMan.GetComponent<TransformComponent>(player).MapUid, Is.EqualTo(nukieMap));
+        Assert.That(entMan.GetComponent<TransformComponent>(rule.NukieOutpost!.Value).MapUid, Is.EqualTo(nukieMap));
+        Assert.That(entMan.GetComponent<TransformComponent>(rule.NukieShuttle!.Value).MapUid, Is.EqualTo(nukieMap));
+
+        // The maps are all map-initialized, including the player
+        // Yes, this is necessary as this has repeatedly been broken somehow.
+        Assert.That(mapSys.IsInitialized(nukieMap));
+        Assert.That(mapSys.IsInitialized(targetMap));
+        Assert.That(mapSys.IsPaused(nukieMap), Is.False);
+        Assert.That(mapSys.IsPaused(targetMap), Is.False);
+
+        EntityLifeStage LifeStage(EntityUid? uid) => entMan.GetComponent<MetaDataComponent>(uid!.Value).EntityLifeStage;
+        Assert.That(LifeStage(player), Is.GreaterThan(EntityLifeStage.Initialized));
+        Assert.That(LifeStage(nukieMap), Is.GreaterThan(EntityLifeStage.Initialized));
+        Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized));
+        Assert.That(LifeStage(rule.NukieOutpost), Is.GreaterThan(EntityLifeStage.Initialized));
+        Assert.That(LifeStage(rule.NukieShuttle), Is.GreaterThan(EntityLifeStage.Initialized));
+        Assert.That(LifeStage(rule.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized));
+
+        // Make sure the player has hands. We've had fucking disarmed nukies before.
+        Assert.That(entMan.HasComponent<HandsComponent>(player));
+        Assert.That(entMan.GetComponent<HandsComponent>(player).Hands.Count, Is.GreaterThan(0));
+
+        // While we're at it, lets make sure they aren't naked. I don't know how many inventory slots all mobs will be
+        // likely to have in the future. But nukies should probably have at least 3 slots with something in them.
+        var enumerator = invSys.GetSlotEnumerator(player);
+        int total = 0;
+        while (enumerator.NextItem(out _))
+        {
+            total++;
+        }
+        Assert.That(total, Is.GreaterThan(3));
+
+        // Finally lets check the nukie commander passed basic training and figured out how to breathe.
+        var totalSeconds = 30;
+        var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds);
+        int increment = 5;
+        var resp = entMan.GetComponent<RespiratorComponent>(player);
+        var damage = entMan.GetComponent<DamageableComponent>(player);
+        for (var tick = 0; tick < totalTicks; tick += increment)
+        {
+            await pair.RunTicksSync(increment);
+            Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
+            Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
+        }
+
+        ticker.SetGamePreset((GamePresetPrototype?)null);
+        server.CfgMan.SetCVar(CCVars.GridFill, false);
+        await pair.CleanReturnAsync();
+    }
+}
index 1e3f9c9854f195ac14d53f039c19b2d52b6acebb..0707bd64c6ffa2bd8e153da9d915aa97dd0adcb1 100644 (file)
@@ -19,6 +19,9 @@ namespace Content.IntegrationTests.Tests.GameRules
             await using var pair = await PoolManager.GetServerClient(new PoolSettings { InLobby = true });
             var server = pair.Server;
 
+            Assert.That(server.EntMan.Count<GameRuleComponent>(), Is.Zero);
+            Assert.That(server.EntMan.Count<ActiveGameRuleComponent>(), Is.Zero);
+
             var entityManager = server.ResolveDependency<IEntityManager>();
             var sGameTicker = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<GameTicker>();
             var sGameTiming = server.ResolveDependency<IGameTiming>();
@@ -26,6 +29,9 @@ namespace Content.IntegrationTests.Tests.GameRules
             sGameTicker.StartGameRule("MaxTimeRestart", out var ruleEntity);
             Assert.That(entityManager.TryGetComponent<MaxTimeRestartRuleComponent>(ruleEntity, out var maxTime));
 
+            Assert.That(server.EntMan.Count<GameRuleComponent>(), Is.EqualTo(1));
+            Assert.That(server.EntMan.Count<ActiveGameRuleComponent>(), Is.EqualTo(1));
+
             await server.WaitAssertion(() =>
             {
                 Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby));
@@ -33,6 +39,9 @@ namespace Content.IntegrationTests.Tests.GameRules
                 sGameTicker.StartRound();
             });
 
+            Assert.That(server.EntMan.Count<GameRuleComponent>(), Is.EqualTo(1));
+            Assert.That(server.EntMan.Count<ActiveGameRuleComponent>(), Is.EqualTo(1));
+
             await server.WaitAssertion(() =>
             {
                 Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.InRound));
index 480fd9cde6fa3c8292a2dccc37346eaf61f11ab5..d45290c8664a13ee42c31d3d43eed38478d229be 100644 (file)
@@ -767,14 +767,9 @@ public abstract partial class InteractionTest
         await Pair.RunTicksSync(ticks);
     }
 
-    protected int SecondsToTicks(float seconds)
-    {
-        return (int) Math.Ceiling(seconds / TickPeriod);
-    }
-
     protected async Task RunSeconds(float seconds)
     {
-        await RunTicks(SecondsToTicks(seconds));
+        await Pair.RunSeconds(seconds);
     }
 
     #endregion
index a4ed31e9983d0281df40922e268da78ce424e94f..42f64b344cdb6af9f3b5861fe3e490c5bd86c521 100644 (file)
@@ -12,7 +12,6 @@ using Content.Shared.Body.Part;
 using Content.Shared.DoAfter;
 using Content.Shared.Hands.Components;
 using Content.Shared.Interaction;
-using Content.Server.Item;
 using Content.Shared.Mind;
 using Content.Shared.Players;
 using Robust.Client.Input;
diff --git a/Content.IntegrationTests/Tests/Mapping/MappingTests.cs b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs
new file mode 100644 (file)
index 0000000..287e30e
--- /dev/null
@@ -0,0 +1,102 @@
+using Robust.Server.GameObjects;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map;
+
+namespace Content.IntegrationTests.Tests.Mapping;
+
+[TestFixture]
+public sealed class MappingTests
+{
+    /// <summary>
+    /// Checks that the mapping command creates paused & uninitialized maps.
+    /// </summary>
+    [Test]
+    public async Task MappingTest()
+    {
+        await using var pair = await PoolManager.GetServerClient(new PoolSettings {Dirty = true, Connected = true, DummyTicker = false});
+
+        var server = pair.Server;
+        var entMan = server.EntMan;
+        var mapSys = server.System<MapSystem>();
+
+        await pair.RunTicksSync(5);
+        var mapId = 1;
+        while (mapSys.MapExists(new(mapId)))
+        {
+            mapId++;
+        }
+
+        await pair.WaitClientCommand($"mapping {mapId}");
+        var map = mapSys.GetMap(new MapId(mapId));
+
+        var mapXform = server.Transform(map);
+        Assert.That(mapXform.MapUid, Is.EqualTo(map));
+        Assert.That(mapXform.MapID, Is.EqualTo(new MapId(mapId)));
+
+        var xform = server.Transform(pair.Player!.AttachedEntity!.Value);
+
+        Assert.That(xform.MapUid, Is.EqualTo(map));
+        Assert.That(mapSys.IsInitialized(map), Is.False);
+        Assert.That(mapSys.IsPaused(map), Is.True);
+        Assert.That(server.MetaData(map).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized));
+        Assert.That(server.MetaData(map).EntityPaused, Is.True);
+
+        // Spawn a new entity
+        EntityUid ent = default;
+        await server.WaitPost(() =>
+        {
+            ent = entMan.Spawn(null, new MapCoordinates(default, new(mapId)));
+        });
+        await pair.RunTicksSync(5);
+        Assert.That(server.MetaData(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized));
+        Assert.That(server.MetaData(ent).EntityPaused, Is.True);
+
+        // Save the map
+        var file = $"{nameof(MappingTest)}.yml";
+        await pair.WaitClientCommand($"savemap {mapId} {file}");
+
+        // Mapinitialize it
+        await pair.WaitClientCommand($"mapinit {mapId}");
+        Assert.That(mapSys.IsInitialized(map), Is.True);
+        Assert.That(mapSys.IsPaused(map), Is.False);
+        Assert.That(server.MetaData(map).EntityLifeStage, Is.EqualTo(EntityLifeStage.MapInitialized));
+        Assert.That(server.MetaData(map).EntityPaused, Is.False);
+        Assert.That(server.MetaData(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.MapInitialized));
+        Assert.That(server.MetaData(ent).EntityPaused, Is.False);
+
+        await server.WaitPost(() => entMan.DeleteEntity(map));
+
+        // Load the saved map
+        mapId++;
+        while (mapSys.MapExists(new(mapId)))
+        {
+            mapId++;
+        }
+
+        await pair.WaitClientCommand($"mapping {mapId} {file}");
+        map = mapSys.GetMap(new MapId(mapId));
+
+        // And it should all be paused and un-initialized
+        xform = server.Transform(pair.Player!.AttachedEntity!.Value);
+        Assert.That(xform.MapUid, Is.EqualTo(map));
+        Assert.That(mapSys.IsInitialized(map), Is.False);
+        Assert.That(mapSys.IsPaused(map), Is.True);
+        Assert.That(server.MetaData(map).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized));
+        Assert.That(server.MetaData(map).EntityPaused, Is.True);
+
+        mapXform = server.Transform(map);
+        Assert.That(mapXform.MapUid, Is.EqualTo(map));
+        Assert.That(mapXform.MapID, Is.EqualTo(new MapId(mapId)));
+        Assert.That(mapXform.ChildCount, Is.EqualTo(2));
+
+        mapXform.ChildEnumerator.MoveNext(out ent);
+        if (ent == pair.Player.AttachedEntity)
+            mapXform.ChildEnumerator.MoveNext(out ent);
+
+        Assert.That(server.MetaData(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized));
+        Assert.That(server.MetaData(ent).EntityPaused, Is.True);
+
+        await server.WaitPost(() => entMan.DeleteEntity(map));
+        await pair.CleanReturnAsync();
+    }
+}
index 5750be09c241b9f5219a178cb21ad126864e651f..532e481ac29574843a3856023d09ece86335bd83 100644 (file)
@@ -28,10 +28,11 @@ public sealed class EvacShuttleTest
         // Dummy ticker tests should not have centcomm
         Assert.That(entMan.Count<StationCentcommComponent>(), Is.Zero);
 
-        var shuttleEnabled = pair.Server.CfgMan.GetCVar(CCVars.EmergencyShuttleEnabled);
-        pair.Server.CfgMan.SetCVar(CCVars.GameMap, "Saltern");
-        pair.Server.CfgMan.SetCVar(CCVars.GameDummyTicker, false);
+        Assert.That(pair.Server.CfgMan.GetCVar(CCVars.GridFill), Is.False);
         pair.Server.CfgMan.SetCVar(CCVars.EmergencyShuttleEnabled, true);
+        pair.Server.CfgMan.SetCVar(CCVars.GameDummyTicker, false);
+        var gameMap = pair.Server.CfgMan.GetCVar(CCVars.GameMap);
+        pair.Server.CfgMan.SetCVar(CCVars.GameMap, "Saltern");
 
         await server.WaitPost(() => ticker.RestartRound());
         await pair.RunTicksSync(25);
@@ -71,6 +72,20 @@ public sealed class EvacShuttleTest
         Assert.That(shuttleXform.MapUid, Is.Not.Null);
         Assert.That(shuttleXform.MapUid, Is.EqualTo(centcommMap));
 
+        // All of these should have been map-initialized.
+        var mapSys = entMan.System<SharedMapSystem>();
+        Assert.That(mapSys.IsInitialized(centcommMap), Is.True);
+        Assert.That(mapSys.IsInitialized(salternXform.MapUid), Is.True);
+        Assert.That(mapSys.IsPaused(centcommMap), Is.False);
+        Assert.That(mapSys.IsPaused(salternXform.MapUid!.Value), Is.False);
+
+        EntityLifeStage LifeStage(EntityUid uid) => entMan.GetComponent<MetaDataComponent>(uid).EntityLifeStage;
+        Assert.That(LifeStage(saltern), Is.EqualTo(EntityLifeStage.MapInitialized));
+        Assert.That(LifeStage(shuttle), Is.EqualTo(EntityLifeStage.MapInitialized));
+        Assert.That(LifeStage(centcomm), Is.EqualTo(EntityLifeStage.MapInitialized));
+        Assert.That(LifeStage(centcommMap), Is.EqualTo(EntityLifeStage.MapInitialized));
+        Assert.That(LifeStage(salternXform.MapUid.Value), Is.EqualTo(EntityLifeStage.MapInitialized));
+
         // Set up shuttle timing
         var evacSys = server.System<EmergencyShuttleSystem>();
         evacSys.TransitTime = ShuttleSystem.DefaultTravelTime; // Absolute minimum transit time, so the test has to run for at least this long
@@ -78,19 +93,15 @@ public sealed class EvacShuttleTest
 
         var dockTime = server.CfgMan.GetCVar(CCVars.EmergencyShuttleDockTime);
         server.CfgMan.SetCVar(CCVars.EmergencyShuttleDockTime, 2);
-        async Task RunSeconds(float seconds)
-        {
-            await pair.RunTicksSync((int) Math.Ceiling(seconds / server.Timing.TickPeriod.TotalSeconds));
-        }
 
         // Call evac shuttle.
         await pair.WaitCommand("callshuttle 0:02");
-        await RunSeconds(3);
+        await pair.RunSeconds(3);
 
         // Shuttle should have arrived on the station
         Assert.That(shuttleXform.MapUid, Is.EqualTo(salternXform.MapUid));
 
-        await RunSeconds(2);
+        await pair.RunSeconds(2);
 
         // Shuttle should be FTLing back to centcomm
         Assert.That(entMan.Count<FTLMapComponent>(), Is.EqualTo(1));
@@ -101,14 +112,15 @@ public sealed class EvacShuttleTest
         Assert.That(shuttleXform.MapUid, Is.EqualTo(ftl.Owner));
 
         // Shuttle should have arrived at centcomm
-        await RunSeconds(ShuttleSystem.DefaultTravelTime);
+        await pair.RunSeconds(ShuttleSystem.DefaultTravelTime);
         Assert.That(shuttleXform.MapUid, Is.EqualTo(centcommMap));
 
         // Round should be ending now
         Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PostRound));
 
         server.CfgMan.SetCVar(CCVars.EmergencyShuttleDockTime, dockTime);
-        pair.Server.CfgMan.SetCVar(CCVars.EmergencyShuttleEnabled, shuttleEnabled);
+        pair.Server.CfgMan.SetCVar(CCVars.EmergencyShuttleEnabled, false);
+        pair.Server.CfgMan.SetCVar(CCVars.GameMap, gameMap);
         await pair.CleanReturnAsync();
     }
 }
index 4290726cc4061f51cd77a745a02ac3e2a1a48fe8..fe53ea268c7a27c429cd365e6b66ed48d2a2a155 100644 (file)
@@ -253,6 +253,8 @@ public sealed class CriminalRecordsConsoleSystem : SharedCriminalRecordsConsoleS
     {
         var name = Identity.Name(uid, EntityManager);
         var xform = Transform(uid);
+
+        // TODO use the entity's station? Not the station of the map that it happens to currently be on?
         var station = _station.GetStationInMap(xform.MapID);
 
         if (station != null && _stationRecords.GetRecordByName(station.Value, name) is { } id)
index b97a16ab99328d4d27c63b6c3d621dc7acf89e82..a1946d34a0aa05bade8c1537d4e6abe208c9187c 100644 (file)
@@ -100,7 +100,7 @@ namespace Content.Server.GameTicking
             SetGamePreset(LobbyEnabled ? _configurationManager.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox");
         }
 
-        public void SetGamePreset(GamePresetPrototype preset, bool force = false)
+        public void SetGamePreset(GamePresetPrototype? preset, bool force = false)
         {
             // Do nothing if this game ticker is a dummy!
             if (DummyTicker)
index 202daf256d22bb5de4d689fe0ff416cf220c42dc..792d838169c26daeca1e5991989719452d0d4eb8 100644 (file)
@@ -165,7 +165,7 @@ namespace Content.Server.GameTicking
 
             var gridIds = _map.LoadMap(targetMapId, ev.GameMap.MapPath.ToString(), ev.Options);
 
-            _metaData.SetEntityName(_mapManager.GetMapEntityId(targetMapId), "Station map");
+            _metaData.SetEntityName(_mapManager.GetMapEntityId(targetMapId), $"station map - {map.MapName}");
 
             var gridUids = gridIds.ToList();
             RaiseLocalEvent(new PostGameMapLoad(map, targetMapId, gridUids, stationName));
index c66a9d12a1c5959c2467ae29d8a7f3acad37c281..8f11e70560f5aaa4ca2cbe88afbd30ad6dca2eaf 100644 (file)
@@ -6,11 +6,7 @@ using Content.Shared.NPC.Prototypes;
 using Content.Shared.Roles;
 using Robust.Shared.Map;
 using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.Utility;
-
 
 namespace Content.Server.GameTicking.Rules.Components;
 
@@ -116,13 +112,14 @@ public sealed partial class NukeopsRuleComponent : Component
     [DataField]
     public List<WinCondition> WinConditions = new ();
 
-    public MapId? NukiePlanet;
-
+    // TODO full game save
     // TODO: use components, don't just cache entity UIDs
     // There have been (and probably still are) bugs where these refer to deleted entities from old rounds.
+    // Whenever this gets fixed, update NukiesTest.
     public EntityUid? NukieOutpost;
     public EntityUid? NukieShuttle;
     public EntityUid? TargetStation;
+    public MapId? NukiePlanet;
 
     /// <summary>
     ///     Data to be used in <see cref="OnMindAdded"/> for an operative once the Mind has been added.
@@ -131,7 +128,7 @@ public sealed partial class NukeopsRuleComponent : Component
     public Dictionary<EntityUid, string> OperativeMindPendingData = new();
 
     [DataField(required: true)]
-    public ProtoId<NpcFactionPrototype> Faction = default!;
+    public ProtoId<NpcFactionPrototype> Faction;
 
     [DataField]
     public NukeopSpawnPreset CommanderSpawnDetails = new() { AntagRoleProto = "NukeopsCommander", GearProto = "SyndicateCommanderGearFull", NamePrefix = "nukeops-role-commander", NameList = "SyndicateNamesElite" };
index e792a004df5e094de65910d3bc66a3db55f31c32..2522ebb53b580e59068c25214b3c01a3c4234c4e 100644 (file)
@@ -33,6 +33,7 @@ public sealed class MaxTimeRestartRuleSystem : GameRuleSystem<MaxTimeRestartRule
 
     public void RestartTimer(MaxTimeRestartRuleComponent component)
     {
+        // TODO FULL GAME SAVE
         component.TimerCancel.Cancel();
         component.TimerCancel = new CancellationTokenSource();
         Timer.Spawn(component.RoundMaxTime, () => TimerFired(component), component.TimerCancel.Token);
@@ -49,6 +50,7 @@ public sealed class MaxTimeRestartRuleSystem : GameRuleSystem<MaxTimeRestartRule
 
         _chatManager.DispatchServerAnnouncement(Loc.GetString("rule-restarting-in-seconds",("seconds", (int) component.RoundEndDelay.TotalSeconds)));
 
+        // TODO FULL GAME SAVE
         Timer.Spawn(component.RoundEndDelay, () => GameTicker.RestartRound());
     }
 
index 08f3dcccf9fb2d44651ee75d970fe7b3329a6f45..46534f7059d62e8177354e6d60e093f4bf951e1c 100644 (file)
@@ -53,7 +53,7 @@ namespace Content.Server.Mapping
             }
 
 #if DEBUG
-            shell.WriteError(Loc.GetString("cmd-mapping-warning"));
+            shell.WriteLine(Loc.GetString("cmd-mapping-warning"));
 #endif
 
             MapId mapId;
index 67f50d7a4e126d24058f93a75874806c90cbe043..58c4c876c508daf69f45c1b1ace8452015104531 100644 (file)
@@ -211,7 +211,7 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem
     /// </remarks>
     public uint? GetRecordByName(EntityUid station, string name, StationRecordsComponent? records = null)
     {
-        if (!Resolve(station, ref records))
+        if (!Resolve(station, ref records, false))
             return null;
 
         foreach (var (id, record) in GetRecordsOfType<GeneralStationRecord>(station, records))
index e8053e4c6787ca86f3390eba63a715fe45bd754f..c25ac1968d519b23e9dc7c3ef5c7c65959b8ef7a 100644 (file)
@@ -137,11 +137,13 @@ public abstract class SharedRoleSystem : EntitySystem
 
     public bool MindHasRole<T>(EntityUid mindId) where T : IComponent
     {
+        DebugTools.Assert(HasComp<MindComponent>(mindId));
         return HasComp<T>(mindId);
     }
 
     public List<RoleInfo> MindGetAllRoles(EntityUid mindId)
     {
+        DebugTools.Assert(HasComp<MindComponent>(mindId));
         var ev = new MindGetAllRolesEvent(new List<RoleInfo>());
         RaiseLocalEvent(mindId, ref ev);
         return ev.Roles;
@@ -152,6 +154,7 @@ public abstract class SharedRoleSystem : EntitySystem
         if (mindId == null)
             return false;
 
+        DebugTools.Assert(HasComp<MindComponent>(mindId));
         var ev = new MindIsAntagonistEvent();
         RaiseLocalEvent(mindId.Value, ref ev);
         return ev.IsAntagonist;