1
0

NukeOpsTest.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. #nullable enable
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Content.Server.Body.Components;
  5. using Content.Server.GameTicking;
  6. using Content.Server.GameTicking.Presets;
  7. using Content.Server.GameTicking.Rules.Components;
  8. using Content.Server.Mind;
  9. using Content.Server.Roles;
  10. using Content.Server.RoundEnd;
  11. using Content.Server.Shuttles.Components;
  12. using Content.Server.Station.Components;
  13. using Content.Shared.CCVar;
  14. using Content.Shared.Damage;
  15. using Content.Shared.FixedPoint;
  16. using Content.Shared.GameTicking;
  17. using Content.Shared.Hands.Components;
  18. using Content.Shared.Inventory;
  19. using Content.Shared.NPC.Systems;
  20. using Content.Shared.NukeOps;
  21. using Content.Shared.Pinpointer;
  22. using Content.Shared.Station.Components;
  23. using Robust.Server.GameObjects;
  24. using Robust.Shared.GameObjects;
  25. using Robust.Shared.Map.Components;
  26. namespace Content.IntegrationTests.Tests.GameRules;
  27. [TestFixture]
  28. public sealed class NukeOpsTest
  29. {
  30. /// <summary>
  31. /// Check that a nuke ops game mode can start without issue. I.e., that the nuke station and such all get loaded.
  32. /// </summary>
  33. [Test]
  34. public async Task TryStopNukeOpsFromConstantlyFailing()
  35. {
  36. await using var pair = await PoolManager.GetServerClient(new PoolSettings
  37. {
  38. Dirty = true,
  39. DummyTicker = false,
  40. Connected = true,
  41. InLobby = true
  42. });
  43. var server = pair.Server;
  44. var client = pair.Client;
  45. var entMan = server.EntMan;
  46. var mapSys = server.System<MapSystem>();
  47. var ticker = server.System<GameTicker>();
  48. var mindSys = server.System<MindSystem>();
  49. var roleSys = server.System<RoleSystem>();
  50. var invSys = server.System<InventorySystem>();
  51. var factionSys = server.System<NpcFactionSystem>();
  52. var roundEndSys = server.System<RoundEndSystem>();
  53. server.CfgMan.SetCVar(CCVars.GridFill, true);
  54. // Initially in the lobby
  55. Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby));
  56. Assert.That(client.AttachedEntity, Is.Null);
  57. Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay));
  58. // Add several dummy players
  59. var dummies = await pair.Server.AddDummySessions(3);
  60. await pair.RunTicksSync(5);
  61. // Opt into the nukies role.
  62. await pair.SetAntagPreference("NukeopsCommander", true);
  63. await pair.SetAntagPreference("NukeopsMedic", true, dummies[1].UserId);
  64. // Initially, the players have no attached entities
  65. Assert.That(pair.Player?.AttachedEntity, Is.Null);
  66. Assert.That(dummies.All(x => x.AttachedEntity == null));
  67. // There are no grids or maps
  68. Assert.That(entMan.Count<MapComponent>(), Is.Zero);
  69. Assert.That(entMan.Count<MapGridComponent>(), Is.Zero);
  70. Assert.That(entMan.Count<StationMapComponent>(), Is.Zero);
  71. Assert.That(entMan.Count<StationMemberComponent>(), Is.Zero);
  72. Assert.That(entMan.Count<StationCentcommComponent>(), Is.Zero);
  73. // And no nukie related components
  74. Assert.That(entMan.Count<NukeopsRuleComponent>(), Is.Zero);
  75. Assert.That(entMan.Count<NukeopsRoleComponent>(), Is.Zero);
  76. Assert.That(entMan.Count<NukeOperativeComponent>(), Is.Zero);
  77. Assert.That(entMan.Count<NukeOpsShuttleComponent>(), Is.Zero);
  78. Assert.That(entMan.Count<NukeOperativeSpawnerComponent>(), Is.Zero);
  79. // Ready up and start nukeops
  80. ticker.ToggleReadyAll(true);
  81. Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.ReadyToPlay));
  82. await pair.WaitCommand("forcepreset Nukeops");
  83. await pair.RunTicksSync(10);
  84. // Game should have started
  85. Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound));
  86. Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.JoinedGame));
  87. Assert.That(client.EntMan.EntityExists(client.AttachedEntity));
  88. var dummyEnts = dummies.Select(x => x.AttachedEntity ?? default).ToArray();
  89. var player = pair.Player!.AttachedEntity!.Value;
  90. Assert.That(entMan.EntityExists(player));
  91. Assert.That(dummyEnts.All(e => entMan.EntityExists(e)));
  92. // Maps now exist
  93. Assert.That(entMan.Count<MapComponent>(), Is.GreaterThan(0));
  94. Assert.That(entMan.Count<MapGridComponent>(), Is.GreaterThan(0));
  95. Assert.That(entMan.Count<StationCentcommComponent>(), Is.EqualTo(1));
  96. // And we now have nukie related components
  97. Assert.That(entMan.Count<NukeopsRuleComponent>(), Is.EqualTo(1));
  98. Assert.That(entMan.Count<NukeopsRoleComponent>(), Is.EqualTo(2));
  99. Assert.That(entMan.Count<NukeOperativeComponent>(), Is.EqualTo(2));
  100. Assert.That(entMan.Count<NukeOpsShuttleComponent>(), Is.EqualTo(1));
  101. // The player entity should be the nukie commander
  102. var mind = mindSys.GetMind(player)!.Value;
  103. Assert.That(entMan.HasComponent<NukeOperativeComponent>(player));
  104. Assert.That(roleSys.MindIsAntagonist(mind));
  105. Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mind));
  106. Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True);
  107. Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False);
  108. var roles = roleSys.MindGetAllRoleInfo(mind);
  109. var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander");
  110. Assert.That(cmdRoles.Count(), Is.EqualTo(1));
  111. // The second dummy player should be a medic
  112. var dummyMind = mindSys.GetMind(dummyEnts[1])!.Value;
  113. Assert.That(entMan.HasComponent<NukeOperativeComponent>(dummyEnts[1]));
  114. Assert.That(roleSys.MindIsAntagonist(dummyMind));
  115. Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(dummyMind));
  116. Assert.That(factionSys.IsMember(dummyEnts[1], "Syndicate"), Is.True);
  117. Assert.That(factionSys.IsMember(dummyEnts[1], "NanoTrasen"), Is.False);
  118. roles = roleSys.MindGetAllRoleInfo(dummyMind);
  119. cmdRoles = roles.Where(x => x.Prototype == "NukeopsMedic");
  120. Assert.That(cmdRoles.Count(), Is.EqualTo(1));
  121. // The other two players should have just spawned in as normal.
  122. CheckDummy(0);
  123. CheckDummy(2);
  124. void CheckDummy(int i)
  125. {
  126. var ent = dummyEnts[i];
  127. var mindCrew = mindSys.GetMind(ent)!.Value;
  128. Assert.That(entMan.HasComponent<NukeOperativeComponent>(ent), Is.False);
  129. Assert.That(roleSys.MindIsAntagonist(mindCrew), Is.False);
  130. Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mindCrew), Is.False);
  131. Assert.That(factionSys.IsMember(ent, "Syndicate"), Is.False);
  132. Assert.That(factionSys.IsMember(ent, "NanoTrasen"), Is.True);
  133. var nukeroles = new List<string>() { "Nukeops", "NukeopsMedic", "NukeopsCommander" };
  134. Assert.That(roleSys.MindGetAllRoleInfo(mindCrew).Any(x => nukeroles.Contains(x.Prototype)), Is.False);
  135. }
  136. // The game rule exists, and all the stations/shuttles/maps are properly initialized
  137. var rule = entMan.AllComponents<NukeopsRuleComponent>().Single();
  138. var ruleComp = rule.Component;
  139. var gridsRule = entMan.AllComponents<RuleGridsComponent>().Single().Component;
  140. foreach (var grid in gridsRule.MapGrids)
  141. {
  142. Assert.That(entMan.EntityExists(grid));
  143. Assert.That(entMan.HasComponent<MapGridComponent>(grid));
  144. }
  145. Assert.That(entMan.EntityExists(ruleComp.TargetStation));
  146. Assert.That(entMan.HasComponent<StationDataComponent>(ruleComp.TargetStation));
  147. var nukieShuttle = entMan.AllComponents<NukeOpsShuttleComponent>().Single();
  148. var nukieShuttlEnt = nukieShuttle.Uid;
  149. Assert.That(entMan.EntityExists(nukieShuttlEnt));
  150. Assert.That(nukieShuttle.Component.AssociatedRule, Is.EqualTo(rule.Uid));
  151. EntityUid? nukieStationEnt = null;
  152. foreach (var grid in gridsRule.MapGrids)
  153. {
  154. if (entMan.HasComponent<StationMemberComponent>(grid))
  155. {
  156. nukieStationEnt = grid;
  157. break;
  158. }
  159. }
  160. Assert.That(!entMan.EntityExists(nukieStationEnt)); // its not supposed to be a station!
  161. Assert.That(server.MapMan.MapExists(gridsRule.Map));
  162. var nukieMap = mapSys.GetMap(gridsRule.Map!.Value);
  163. var targetStation = entMan.GetComponent<StationDataComponent>(ruleComp.TargetStation!.Value);
  164. var targetGrid = targetStation.Grids.First();
  165. var targetMap = entMan.GetComponent<TransformComponent>(targetGrid).MapUid!.Value;
  166. Assert.That(targetMap, Is.Not.EqualTo(nukieMap));
  167. Assert.That(entMan.GetComponent<TransformComponent>(player).MapUid, Is.EqualTo(nukieMap));
  168. Assert.That(entMan.GetComponent<TransformComponent>(nukieShuttlEnt).MapUid, Is.EqualTo(nukieMap));
  169. // The maps are all map-initialized, including the player
  170. // Yes, this is necessary as this has repeatedly been broken somehow.
  171. Assert.That(mapSys.IsInitialized(nukieMap));
  172. Assert.That(mapSys.IsInitialized(targetMap));
  173. Assert.That(mapSys.IsPaused(nukieMap), Is.False);
  174. Assert.That(mapSys.IsPaused(targetMap), Is.False);
  175. EntityLifeStage LifeStage(EntityUid? uid) => entMan.GetComponent<MetaDataComponent>(uid!.Value).EntityLifeStage;
  176. Assert.That(LifeStage(player), Is.GreaterThan(EntityLifeStage.Initialized));
  177. Assert.That(LifeStage(nukieMap), Is.GreaterThan(EntityLifeStage.Initialized));
  178. Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized));
  179. Assert.That(LifeStage(nukieShuttlEnt), Is.GreaterThan(EntityLifeStage.Initialized));
  180. Assert.That(LifeStage(ruleComp.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized));
  181. // Make sure the player has hands. We've had fucking disarmed nukies before.
  182. Assert.That(entMan.HasComponent<HandsComponent>(player));
  183. Assert.That(entMan.GetComponent<HandsComponent>(player).Hands.Count, Is.GreaterThan(0));
  184. // While we're at it, lets make sure they aren't naked. I don't know how many inventory slots all mobs will be
  185. // likely to have in the future. But nukies should probably have at least 3 slots with something in them.
  186. var enumerator = invSys.GetSlotEnumerator(player);
  187. var total = 0;
  188. while (enumerator.NextItem(out _))
  189. {
  190. total++;
  191. }
  192. Assert.That(total, Is.GreaterThan(3));
  193. // Check the nukie commander passed basic training and figured out how to breathe.
  194. var totalSeconds = 30;
  195. var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds);
  196. var increment = 5;
  197. var resp = entMan.GetComponent<RespiratorComponent>(player);
  198. var damage = entMan.GetComponent<DamageableComponent>(player);
  199. for (var tick = 0; tick < totalTicks; tick += increment)
  200. {
  201. await pair.RunTicksSync(increment);
  202. Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
  203. Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
  204. }
  205. // Check that the round does not end prematurely when agents are deleted in the outpost
  206. var nukies = dummyEnts.Where(entMan.HasComponent<NukeOperativeComponent>).Append(player).ToArray();
  207. await server.WaitAssertion(() =>
  208. {
  209. for (var i = 0; i < nukies.Length - 1; i++)
  210. {
  211. entMan.DeleteEntity(nukies[i]);
  212. Assert.That(roundEndSys.IsRoundEndRequested,
  213. Is.False,
  214. $"The round ended, but {nukies.Length - i - 1} nukies are still alive!");
  215. }
  216. // Delete the last nukie and make sure the round ends.
  217. entMan.DeleteEntity(nukies[^1]);
  218. Assert.That(roundEndSys.IsRoundEndRequested,
  219. "All nukies were deleted, but the round didn't end!");
  220. });
  221. ticker.SetGamePreset((GamePresetPrototype?) null);
  222. await pair.CleanReturnAsync();
  223. }
  224. }