MindTests.EntityDeletion.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. #nullable enable
  2. using System.Linq;
  3. using Content.Server.GameTicking;
  4. using Content.Shared.Ghost;
  5. using Content.Shared.Mind;
  6. using Content.Shared.Players;
  7. using Robust.Server.Console;
  8. using Robust.Server.GameObjects;
  9. using Robust.Server.Player;
  10. using Robust.Shared.GameObjects;
  11. using Robust.Shared.Map;
  12. namespace Content.IntegrationTests.Tests.Minds;
  13. // Tests various scenarios where an entity that is associated with a player's mind is deleted.
  14. public sealed partial class MindTests
  15. {
  16. // This test will do the following:
  17. // - spawn a player
  18. // - visit some entity
  19. // - delete the entity being visited
  20. // - assert that player returns to original entity
  21. [Test]
  22. public async Task TestDeleteVisiting()
  23. {
  24. await using var pair = await SetupPair();
  25. var server = pair.Server;
  26. var entMan = server.ResolveDependency<IServerEntityManager>();
  27. var playerMan = server.ResolveDependency<IPlayerManager>();
  28. var mindSystem = entMan.EntitySysManager.GetEntitySystem<SharedMindSystem>();
  29. EntityUid playerEnt = default;
  30. EntityUid visitEnt = default;
  31. EntityUid mindId = default!;
  32. MindComponent mind = default!;
  33. await server.WaitAssertion(() =>
  34. {
  35. var player = playerMan.Sessions.Single();
  36. playerEnt = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
  37. visitEnt = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
  38. mindId = mindSystem.CreateMind(player.UserId);
  39. mind = entMan.GetComponent<MindComponent>(mindId);
  40. mindSystem.TransferTo(mindId, playerEnt);
  41. mindSystem.Visit(mindId, visitEnt);
  42. Assert.Multiple(() =>
  43. {
  44. Assert.That(player.AttachedEntity, Is.EqualTo(visitEnt));
  45. Assert.That(mind.VisitingEntity, Is.EqualTo(visitEnt));
  46. });
  47. });
  48. await pair.RunTicksSync(5);
  49. await server.WaitPost(() => entMan.DeleteEntity(visitEnt));
  50. await pair.RunTicksSync(5);
  51. #pragma warning disable NUnit2045 // Interdependent assertions.
  52. Assert.That(mind.VisitingEntity, Is.Null);
  53. Assert.That(entMan.EntityExists(mind.OwnedEntity));
  54. Assert.That(mind.OwnedEntity, Is.EqualTo(playerEnt));
  55. #pragma warning restore NUnit2045
  56. // This used to throw so make sure it doesn't.
  57. await server.WaitPost(() => entMan.DeleteEntity(mind.OwnedEntity!.Value));
  58. await pair.RunTicksSync(5);
  59. await pair.CleanReturnAsync();
  60. }
  61. // this is a variant of TestGhostOnDelete that just deletes the whole map.
  62. [Test]
  63. public async Task TestGhostOnDeleteMap()
  64. {
  65. await using var pair = await SetupPair(dirty: true);
  66. var server = pair.Server;
  67. var testMap = await pair.CreateTestMap();
  68. var testMap2 = await pair.CreateTestMap();
  69. var entMan = server.ResolveDependency<IServerEntityManager>();
  70. var mapSystem = server.System<SharedMapSystem>();
  71. var playerMan = server.ResolveDependency<IPlayerManager>();
  72. var player = playerMan.Sessions.Single();
  73. var mindSystem = entMan.EntitySysManager.GetEntitySystem<SharedMindSystem>();
  74. EntityUid playerEnt = default;
  75. EntityUid mindId = default!;
  76. MindComponent mind = default!;
  77. await server.WaitAssertion(() =>
  78. {
  79. playerEnt = entMan.SpawnEntity(null, testMap.GridCoords);
  80. mindId = player.ContentData()!.Mind!.Value;
  81. mind = entMan.GetComponent<MindComponent>(mindId);
  82. mindSystem.TransferTo(mindId, playerEnt);
  83. Assert.That(mind.CurrentEntity, Is.EqualTo(playerEnt));
  84. });
  85. await pair.RunTicksSync(5);
  86. await server.WaitAssertion(() => mapSystem.DeleteMap(testMap.MapId));
  87. await pair.RunTicksSync(5);
  88. await server.WaitAssertion(() =>
  89. {
  90. #pragma warning disable NUnit2045 // Interdependent assertions.
  91. // Spawn ghost on the second map
  92. var attachedEntity = player.AttachedEntity;
  93. Assert.That(entMan.EntityExists(attachedEntity), Is.True);
  94. Assert.That(attachedEntity, Is.Not.EqualTo(playerEnt));
  95. Assert.That(entMan.HasComponent<GhostComponent>(attachedEntity));
  96. var transform = entMan.GetComponent<TransformComponent>(attachedEntity.Value);
  97. Assert.That(transform.MapID, Is.Not.EqualTo(MapId.Nullspace));
  98. Assert.That(transform.MapID, Is.Not.EqualTo(testMap.MapId));
  99. #pragma warning restore NUnit2045
  100. });
  101. await pair.CleanReturnAsync();
  102. }
  103. /// <summary>
  104. /// Test that a ghost gets created when the player entity is deleted.
  105. /// 1. Delete mob
  106. /// 2. Assert is ghost
  107. /// </summary>
  108. [Test]
  109. public async Task TestGhostOnDelete()
  110. {
  111. // Client is needed to spawn session
  112. await using var pair = await SetupPair(dirty: true);
  113. var server = pair.Server;
  114. var entMan = server.ResolveDependency<IServerEntityManager>();
  115. var playerMan = server.ResolveDependency<IPlayerManager>();
  116. var player = playerMan.Sessions.Single();
  117. Assert.That(!entMan.HasComponent<GhostComponent>(player.AttachedEntity), "Player was initially a ghost?");
  118. // Delete entity
  119. await server.WaitPost(() => entMan.DeleteEntity(player.AttachedEntity!.Value));
  120. await pair.RunTicksSync(5);
  121. Assert.That(entMan.HasComponent<GhostComponent>(player.AttachedEntity), "Player did not become a ghost");
  122. await pair.CleanReturnAsync();
  123. }
  124. /// <summary>
  125. /// Test that when the original mob gets deleted, the visited ghost does not get deleted.
  126. /// And that the visited ghost becomes the main mob.
  127. /// 1. Visit ghost
  128. /// 2. Delete original mob
  129. /// 3. Assert is ghost
  130. /// 4. Assert was not deleted
  131. /// 5. Assert is main mob
  132. /// </summary>
  133. [Test]
  134. public async Task TestOriginalDeletedWhileGhostingKeepsGhost()
  135. {
  136. // Client is needed to spawn session
  137. await using var pair = await SetupPair();
  138. var server = pair.Server;
  139. var entMan = server.ResolveDependency<IServerEntityManager>();
  140. var playerMan = server.ResolveDependency<IPlayerManager>();
  141. var mindSystem = entMan.EntitySysManager.GetEntitySystem<SharedMindSystem>();
  142. var mind = GetMind(pair);
  143. var player = playerMan.Sessions.Single();
  144. #pragma warning disable NUnit2045 // Interdependent assertions.
  145. Assert.That(player.AttachedEntity, Is.Not.Null);
  146. Assert.That(entMan.EntityExists(player.AttachedEntity));
  147. #pragma warning restore NUnit2045
  148. var originalEntity = player.AttachedEntity.Value;
  149. EntityUid ghost = default!;
  150. await server.WaitAssertion(() =>
  151. {
  152. ghost = entMan.SpawnEntity(GameTicker.ObserverPrototypeName, MapCoordinates.Nullspace);
  153. mindSystem.Visit(mind.Id, ghost);
  154. });
  155. Assert.Multiple(() =>
  156. {
  157. Assert.That(player.AttachedEntity, Is.EqualTo(ghost));
  158. Assert.That(entMan.HasComponent<GhostComponent>(player.AttachedEntity), "player is not a ghost");
  159. Assert.That(mind.Comp.VisitingEntity, Is.EqualTo(player.AttachedEntity));
  160. Assert.That(mind.Comp.OwnedEntity, Is.EqualTo(originalEntity));
  161. });
  162. await pair.RunTicksSync(5);
  163. await server.WaitAssertion(() => entMan.DeleteEntity(originalEntity));
  164. await pair.RunTicksSync(5);
  165. Assert.That(entMan.Deleted(originalEntity));
  166. // Check that the player is still in control of the ghost
  167. mind = GetMind(pair);
  168. Assert.That(!entMan.Deleted(ghost), "ghost has been deleted");
  169. Assert.Multiple(() =>
  170. {
  171. Assert.That(player.AttachedEntity, Is.EqualTo(ghost));
  172. Assert.That(entMan.HasComponent<GhostComponent>(player.AttachedEntity));
  173. Assert.That(mind.Comp.VisitingEntity, Is.Null);
  174. Assert.That(mind.Comp.OwnedEntity, Is.EqualTo(ghost));
  175. });
  176. await pair.CleanReturnAsync();
  177. }
  178. /// <summary>
  179. /// Test that ghosts can become admin ghosts without issue
  180. /// 1. Become a ghost
  181. /// 2. visit an admin ghost
  182. /// 3. original ghost is deleted, player is an admin ghost.
  183. /// </summary>
  184. [Test]
  185. public async Task TestGhostToAghost()
  186. {
  187. await using var pair = await SetupPair();
  188. var server = pair.Server;
  189. var entMan = server.ResolveDependency<IServerEntityManager>();
  190. var playerMan = server.ResolveDependency<IPlayerManager>();
  191. var serverConsole = server.ResolveDependency<IServerConsoleHost>();
  192. var player = playerMan.Sessions.Single();
  193. var ghost = await BecomeGhost(pair);
  194. // Player is a normal ghost (not admin ghost).
  195. Assert.That(entMan.GetComponent<MetaDataComponent>(player.AttachedEntity!.Value).EntityPrototype?.ID, Is.Not.EqualTo(GameTicker.AdminObserverPrototypeName));
  196. // Try to become an admin ghost
  197. await server.WaitAssertion(() => serverConsole.ExecuteCommand(player, "aghost"));
  198. await pair.RunTicksSync(5);
  199. Assert.That(entMan.Deleted(ghost), "old ghost was not deleted");
  200. Assert.Multiple(() =>
  201. {
  202. Assert.That(player.AttachedEntity, Is.Not.EqualTo(ghost), "Player is still attached to the old ghost");
  203. Assert.That(entMan.HasComponent<GhostComponent>(player.AttachedEntity), "Player did not become a new ghost");
  204. Assert.That(entMan.GetComponent<MetaDataComponent>(player.AttachedEntity!.Value).EntityPrototype?.ID, Is.EqualTo(GameTicker.AdminObserverPrototypeName));
  205. });
  206. var mindId = player.ContentData()?.Mind;
  207. Assert.That(mindId, Is.Not.Null);
  208. var mind = entMan.GetComponent<MindComponent>(mindId.Value);
  209. Assert.That(mind.VisitingEntity, Is.Null);
  210. await pair.CleanReturnAsync();
  211. }
  212. /// <summary>
  213. /// Test ghost getting deleted while player is connected spawns another ghost
  214. /// 1. become ghost
  215. /// 2. delete ghost
  216. /// 3. new ghost is spawned
  217. /// </summary>
  218. [Test]
  219. public async Task TestGhostDeletedSpawnsNewGhost()
  220. {
  221. // Client is needed to spawn session
  222. await using var pair = await SetupPair();
  223. var server = pair.Server;
  224. var entMan = server.ResolveDependency<IServerEntityManager>();
  225. var playerMan = server.ResolveDependency<IPlayerManager>();
  226. var serverConsole = server.ResolveDependency<IServerConsoleHost>();
  227. var player = playerMan.Sessions.Single();
  228. EntityUid ghost = default!;
  229. await server.WaitAssertion(() =>
  230. {
  231. Assert.That(player.AttachedEntity, Is.Not.EqualTo(null));
  232. entMan.DeleteEntity(player.AttachedEntity!.Value);
  233. });
  234. await pair.RunTicksSync(5);
  235. await server.WaitAssertion(() =>
  236. {
  237. // Is player a ghost?
  238. Assert.That(player.AttachedEntity, Is.Not.EqualTo(null));
  239. ghost = player.AttachedEntity!.Value;
  240. Assert.That(entMan.HasComponent<GhostComponent>(ghost));
  241. });
  242. await pair.RunTicksSync(5);
  243. await server.WaitAssertion(() =>
  244. {
  245. serverConsole.ExecuteCommand(player, "aghost");
  246. });
  247. await pair.RunTicksSync(5);
  248. await server.WaitAssertion(() =>
  249. {
  250. #pragma warning disable NUnit2045 // Interdependent assertions.
  251. Assert.That(entMan.Deleted(ghost));
  252. Assert.That(player.AttachedEntity, Is.Not.EqualTo(ghost));
  253. Assert.That(entMan.HasComponent<GhostComponent>(player.AttachedEntity!.Value));
  254. #pragma warning restore NUnit2045
  255. });
  256. await pair.CleanReturnAsync();
  257. }
  258. }