StorageTest.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #nullable enable
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Content.Server.Storage.Components;
  5. using Content.Shared.Item;
  6. using Content.Shared.Prototypes;
  7. using Content.Shared.Storage;
  8. using Content.Shared.Storage.Components;
  9. using Content.Shared.Storage.EntitySystems;
  10. using Robust.Shared.GameObjects;
  11. using Robust.Shared.Prototypes;
  12. namespace Content.IntegrationTests.Tests
  13. {
  14. [TestFixture]
  15. public sealed class StorageTest
  16. {
  17. /// <summary>
  18. /// Can an item store more than itself weighs.
  19. /// In an ideal world this test wouldn't need to exist because sizes would be recursive.
  20. /// </summary>
  21. [Test]
  22. public async Task StorageSizeArbitrageTest()
  23. {
  24. await using var pair = await PoolManager.GetServerClient();
  25. var server = pair.Server;
  26. var protoManager = server.ResolveDependency<IPrototypeManager>();
  27. var entMan = server.ResolveDependency<IEntityManager>();
  28. var itemSys = entMan.System<SharedItemSystem>();
  29. await server.WaitAssertion(() =>
  30. {
  31. foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
  32. {
  33. if (!proto.TryGetComponent<StorageComponent>("Storage", out var storage) ||
  34. storage.Whitelist != null ||
  35. storage.MaxItemSize == null ||
  36. !proto.TryGetComponent<ItemComponent>("Item", out var item))
  37. continue;
  38. Assert.That(itemSys.GetSizePrototype(storage.MaxItemSize.Value).Weight,
  39. Is.LessThanOrEqualTo(itemSys.GetSizePrototype(item.Size).Weight),
  40. $"Found storage arbitrage on {proto.ID}");
  41. }
  42. });
  43. await pair.CleanReturnAsync();
  44. }
  45. [Test]
  46. public async Task TestStorageFillPrototypes()
  47. {
  48. await using var pair = await PoolManager.GetServerClient();
  49. var server = pair.Server;
  50. var protoManager = server.ResolveDependency<IPrototypeManager>();
  51. await server.WaitAssertion(() =>
  52. {
  53. Assert.Multiple(() =>
  54. {
  55. foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
  56. {
  57. if (!proto.TryGetComponent<StorageFillComponent>("StorageFill", out var storage))
  58. continue;
  59. foreach (var entry in storage.Contents)
  60. {
  61. Assert.That(entry.Amount, Is.GreaterThan(0), $"Specified invalid amount of {entry.Amount} for prototype {proto.ID}");
  62. Assert.That(entry.SpawnProbability, Is.GreaterThan(0), $"Specified invalid probability of {entry.SpawnProbability} for prototype {proto.ID}");
  63. }
  64. }
  65. });
  66. });
  67. await pair.CleanReturnAsync();
  68. }
  69. [Test]
  70. public async Task TestSufficientSpaceForFill()
  71. {
  72. await using var pair = await PoolManager.GetServerClient();
  73. var server = pair.Server;
  74. var entMan = server.ResolveDependency<IEntityManager>();
  75. var protoMan = server.ResolveDependency<IPrototypeManager>();
  76. var compFact = server.ResolveDependency<IComponentFactory>();
  77. var id = compFact.GetComponentName(typeof(StorageFillComponent));
  78. var itemSys = entMan.System<SharedItemSystem>();
  79. var allSizes = protoMan.EnumeratePrototypes<ItemSizePrototype>().ToList();
  80. allSizes.Sort();
  81. await Assert.MultipleAsync(async () =>
  82. {
  83. foreach (var (proto, fill) in pair.GetPrototypesWithComponent<StorageFillComponent>())
  84. {
  85. if (proto.HasComponent<EntityStorageComponent>(compFact))
  86. continue;
  87. StorageComponent? storage = null;
  88. ItemComponent? item = null;
  89. var size = 0;
  90. await server.WaitAssertion(() =>
  91. {
  92. if (!proto.TryGetComponent("Storage", out storage))
  93. {
  94. Assert.Fail($"Entity {proto.ID} has storage-fill without a storage component!");
  95. return;
  96. }
  97. proto.TryGetComponent("Item", out item);
  98. size = GetFillSize(fill, false, protoMan, itemSys);
  99. });
  100. if (storage == null)
  101. continue;
  102. var maxSize = storage.MaxItemSize;
  103. if (storage.MaxItemSize == null)
  104. {
  105. if (item?.Size == null)
  106. {
  107. maxSize = SharedStorageSystem.DefaultStorageMaxItemSize;
  108. }
  109. else
  110. {
  111. var curIndex = allSizes.IndexOf(protoMan.Index(item.Size));
  112. var index = Math.Max(0, curIndex - 1);
  113. maxSize = allSizes[index].ID;
  114. }
  115. }
  116. if (maxSize == null)
  117. continue;
  118. Assert.That(size, Is.LessThanOrEqualTo(storage.Grid.GetArea()), $"{proto.ID} storage fill is too large.");
  119. foreach (var entry in fill.Contents)
  120. {
  121. if (entry.PrototypeId == null)
  122. continue;
  123. if (!protoMan.TryIndex<EntityPrototype>(entry.PrototypeId, out var fillItem))
  124. continue;
  125. ItemComponent? entryItem = null;
  126. await server.WaitPost(() =>
  127. {
  128. fillItem.TryGetComponent("Item", out entryItem);
  129. });
  130. if (entryItem == null)
  131. continue;
  132. Assert.That(protoMan.Index(entryItem.Size).Weight,
  133. Is.LessThanOrEqualTo(protoMan.Index(maxSize.Value).Weight),
  134. $"Entity {proto.ID} has storage-fill item, {entry.PrototypeId}, that is too large");
  135. }
  136. }
  137. });
  138. await pair.CleanReturnAsync();
  139. }
  140. [Test]
  141. public async Task TestSufficientSpaceForEntityStorageFill()
  142. {
  143. await using var pair = await PoolManager.GetServerClient();
  144. var server = pair.Server;
  145. var entMan = server.ResolveDependency<IEntityManager>();
  146. var protoMan = server.ResolveDependency<IPrototypeManager>();
  147. var compFact = server.ResolveDependency<IComponentFactory>();
  148. var id = compFact.GetComponentName(typeof(StorageFillComponent));
  149. var itemSys = entMan.System<SharedItemSystem>();
  150. foreach (var (proto, fill) in pair.GetPrototypesWithComponent<StorageFillComponent>())
  151. {
  152. if (proto.HasComponent<StorageComponent>(compFact))
  153. continue;
  154. await server.WaitAssertion(() =>
  155. {
  156. if (!proto.TryGetComponent("EntityStorage", out EntityStorageComponent? entStorage))
  157. Assert.Fail($"Entity {proto.ID} has storage-fill without a storage component!");
  158. if (entStorage == null)
  159. return;
  160. var size = GetFillSize(fill, true, protoMan, itemSys);
  161. Assert.That(size, Is.LessThanOrEqualTo(entStorage.Capacity),
  162. $"{proto.ID} storage fill is too large.");
  163. });
  164. }
  165. await pair.CleanReturnAsync();
  166. }
  167. private int GetEntrySize(EntitySpawnEntry entry, bool getCount, IPrototypeManager protoMan, SharedItemSystem itemSystem)
  168. {
  169. if (entry.PrototypeId == null)
  170. return 0;
  171. if (!protoMan.TryIndex<EntityPrototype>(entry.PrototypeId, out var proto))
  172. {
  173. Assert.Fail($"Unknown prototype: {entry.PrototypeId}");
  174. return 0;
  175. }
  176. if (getCount)
  177. return entry.Amount;
  178. if (proto.TryGetComponent<ItemComponent>("Item", out var item))
  179. return itemSystem.GetItemShape(item).GetArea() * entry.Amount;
  180. Assert.Fail($"Prototype is missing item comp: {entry.PrototypeId}");
  181. return 0;
  182. }
  183. private int GetFillSize(StorageFillComponent fill, bool getCount, IPrototypeManager protoMan, SharedItemSystem itemSystem)
  184. {
  185. var totalSize = 0;
  186. var groups = new Dictionary<string, int>();
  187. foreach (var entry in fill.Contents)
  188. {
  189. var size = GetEntrySize(entry, getCount, protoMan, itemSystem);
  190. if (entry.GroupId == null)
  191. totalSize += size;
  192. else
  193. groups[entry.GroupId] = Math.Max(size, groups.GetValueOrDefault(entry.GroupId));
  194. }
  195. return totalSize + groups.Values.Sum();
  196. }
  197. }
  198. }