1
0

NukeOpsTest.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. var ruleGridComps = entMan.AllComponents<RuleGridsComponent>();
  137. Assert.That(ruleGridComps, Has.Length.EqualTo(1),
  138. $"Unexpected RuleGrid(s) detected! {string.Join(',', ruleGridComps.Select(e => server.EntMan.ToPrettyString(e.Uid)))}");
  139. // The game rule exists, and all the stations/shuttles/maps are properly initialized
  140. var rule = entMan.AllComponents<NukeopsRuleComponent>().Single();
  141. var ruleComp = rule.Component;
  142. var gridsRule = ruleGridComps.Single().Component;
  143. foreach (var grid in gridsRule.MapGrids)
  144. {
  145. Assert.That(entMan.EntityExists(grid));
  146. Assert.That(entMan.HasComponent<MapGridComponent>(grid));
  147. }
  148. Assert.That(entMan.EntityExists(ruleComp.TargetStation));
  149. Assert.That(entMan.HasComponent<StationDataComponent>(ruleComp.TargetStation));
  150. var nukieShuttle = entMan.AllComponents<NukeOpsShuttleComponent>().Single();
  151. var nukieShuttlEnt = nukieShuttle.Uid;
  152. Assert.That(entMan.EntityExists(nukieShuttlEnt));
  153. Assert.That(nukieShuttle.Component.AssociatedRule, Is.EqualTo(rule.Uid));
  154. EntityUid? nukieStationEnt = null;
  155. foreach (var grid in gridsRule.MapGrids)
  156. {
  157. if (entMan.HasComponent<StationMemberComponent>(grid))
  158. {
  159. nukieStationEnt = grid;
  160. break;
  161. }
  162. }
  163. Assert.That(!entMan.EntityExists(nukieStationEnt)); // its not supposed to be a station!
  164. Assert.That(server.MapMan.MapExists(gridsRule.Map));
  165. var nukieMap = mapSys.GetMap(gridsRule.Map!.Value);
  166. var targetStation = entMan.GetComponent<StationDataComponent>(ruleComp.TargetStation!.Value);
  167. var targetGrid = targetStation.Grids.First();
  168. var targetMap = entMan.GetComponent<TransformComponent>(targetGrid).MapUid!.Value;
  169. Assert.That(targetMap, Is.Not.EqualTo(nukieMap));
  170. Assert.That(entMan.GetComponent<TransformComponent>(player).MapUid, Is.EqualTo(nukieMap));
  171. Assert.That(entMan.GetComponent<TransformComponent>(nukieShuttlEnt).MapUid, Is.EqualTo(nukieMap));
  172. // The maps are all map-initialized, including the player
  173. // Yes, this is necessary as this has repeatedly been broken somehow.
  174. Assert.That(mapSys.IsInitialized(nukieMap));
  175. Assert.That(mapSys.IsInitialized(targetMap));
  176. Assert.That(mapSys.IsPaused(nukieMap), Is.False);
  177. Assert.That(mapSys.IsPaused(targetMap), Is.False);
  178. EntityLifeStage LifeStage(EntityUid? uid) => entMan.GetComponent<MetaDataComponent>(uid!.Value).EntityLifeStage;
  179. Assert.That(LifeStage(player), Is.GreaterThan(EntityLifeStage.Initialized));
  180. Assert.That(LifeStage(nukieMap), Is.GreaterThan(EntityLifeStage.Initialized));
  181. Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized));
  182. Assert.That(LifeStage(nukieShuttlEnt), Is.GreaterThan(EntityLifeStage.Initialized));
  183. Assert.That(LifeStage(ruleComp.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized));
  184. // Make sure the player has hands. We've had fucking disarmed nukies before.
  185. Assert.That(entMan.HasComponent<HandsComponent>(player));
  186. Assert.That(entMan.GetComponent<HandsComponent>(player).Hands.Count, Is.GreaterThan(0));
  187. // While we're at it, lets make sure they aren't naked. I don't know how many inventory slots all mobs will be
  188. // likely to have in the future. But nukies should probably have at least 3 slots with something in them.
  189. var enumerator = invSys.GetSlotEnumerator(player);
  190. var total = 0;
  191. while (enumerator.NextItem(out _))
  192. {
  193. total++;
  194. }
  195. Assert.That(total, Is.GreaterThan(3));
  196. // Check the nukie commander passed basic training and figured out how to breathe.
  197. var totalSeconds = 30;
  198. var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds);
  199. var increment = 5;
  200. var resp = entMan.GetComponent<RespiratorComponent>(player);
  201. var damage = entMan.GetComponent<DamageableComponent>(player);
  202. for (var tick = 0; tick < totalTicks; tick += increment)
  203. {
  204. await pair.RunTicksSync(increment);
  205. Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
  206. Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
  207. }
  208. // Check that the round does not end prematurely when agents are deleted in the outpost
  209. var nukies = dummyEnts.Where(entMan.HasComponent<NukeOperativeComponent>).Append(player).ToArray();
  210. await server.WaitAssertion(() =>
  211. {
  212. for (var i = 0; i < nukies.Length - 1; i++)
  213. {
  214. entMan.DeleteEntity(nukies[i]);
  215. Assert.That(roundEndSys.IsRoundEndRequested,
  216. Is.False,
  217. $"The round ended, but {nukies.Length - i - 1} nukies are still alive!");
  218. }
  219. // Delete the last nukie and make sure the round ends.
  220. entMan.DeleteEntity(nukies[^1]);
  221. Assert.That(roundEndSys.IsRoundEndRequested,
  222. "All nukies were deleted, but the round didn't end!");
  223. });
  224. ticker.SetGamePreset((GamePresetPrototype?) null);
  225. await pair.CleanReturnAsync();
  226. }
  227. }