1
0

CargoTest.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using System.Numerics;
  4. using Content.Server.Cargo.Components;
  5. using Content.Server.Cargo.Systems;
  6. using Content.Server.Nutrition.Components;
  7. using Content.Server.Nutrition.EntitySystems;
  8. using Content.Shared.Cargo.Prototypes;
  9. using Content.Shared.Prototypes;
  10. using Content.Shared.Stacks;
  11. using Content.Shared.Whitelist;
  12. using Robust.Shared.GameObjects;
  13. using Robust.Shared.Map;
  14. using Robust.Shared.Prototypes;
  15. namespace Content.IntegrationTests.Tests;
  16. [TestFixture]
  17. public sealed class CargoTest
  18. {
  19. private static readonly HashSet<ProtoId<CargoProductPrototype>> Ignored =
  20. [
  21. // This is ignored because it is explicitly intended to be able to sell for more than it costs.
  22. new("FunCrateGambling")
  23. ];
  24. [Test]
  25. public async Task NoCargoOrderArbitrage()
  26. {
  27. await using var pair = await PoolManager.GetServerClient();
  28. var server = pair.Server;
  29. var testMap = await pair.CreateTestMap();
  30. var entManager = server.ResolveDependency<IEntityManager>();
  31. var protoManager = server.ResolveDependency<IPrototypeManager>();
  32. var pricing = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<PricingSystem>();
  33. await server.WaitAssertion(() =>
  34. {
  35. Assert.Multiple(() =>
  36. {
  37. foreach (var proto in protoManager.EnumeratePrototypes<CargoProductPrototype>())
  38. {
  39. if (Ignored.Contains(proto.ID))
  40. continue;
  41. var ent = entManager.SpawnEntity(proto.Product, testMap.MapCoords);
  42. var price = pricing.GetPrice(ent);
  43. Assert.That(price, Is.AtMost(proto.Cost), $"Found arbitrage on {proto.ID} cargo product! Cost is {proto.Cost} but sell is {price}!");
  44. entManager.DeleteEntity(ent);
  45. }
  46. });
  47. });
  48. await pair.CleanReturnAsync();
  49. }
  50. [Test]
  51. public async Task NoCargoBountyArbitrageTest()
  52. {
  53. await using var pair = await PoolManager.GetServerClient();
  54. var server = pair.Server;
  55. var testMap = await pair.CreateTestMap();
  56. var entManager = server.ResolveDependency<IEntityManager>();
  57. var mapSystem = server.System<SharedMapSystem>();
  58. var protoManager = server.ResolveDependency<IPrototypeManager>();
  59. var cargo = entManager.System<CargoSystem>();
  60. var bounties = protoManager.EnumeratePrototypes<CargoBountyPrototype>().ToList();
  61. await server.WaitAssertion(() =>
  62. {
  63. var mapId = testMap.MapId;
  64. Assert.Multiple(() =>
  65. {
  66. foreach (var proto in protoManager.EnumeratePrototypes<CargoProductPrototype>())
  67. {
  68. var ent = entManager.SpawnEntity(proto.Product, new MapCoordinates(Vector2.Zero, mapId));
  69. foreach (var bounty in bounties)
  70. {
  71. if (cargo.IsBountyComplete(ent, bounty))
  72. Assert.That(proto.Cost, Is.GreaterThanOrEqualTo(bounty.Reward), $"Found arbitrage on {bounty.ID} cargo bounty! Product {proto.ID} costs {proto.Cost} but fulfills bounty {bounty.ID} with reward {bounty.Reward}!");
  73. }
  74. entManager.DeleteEntity(ent);
  75. }
  76. });
  77. mapSystem.DeleteMap(mapId);
  78. });
  79. await pair.CleanReturnAsync();
  80. }
  81. [Test]
  82. public async Task NoStaticPriceAndStackPrice()
  83. {
  84. await using var pair = await PoolManager.GetServerClient();
  85. var server = pair.Server;
  86. var protoManager = server.ProtoMan;
  87. var compFact = server.ResolveDependency<IComponentFactory>();
  88. await server.WaitAssertion(() =>
  89. {
  90. var protoIds = protoManager.EnumeratePrototypes<EntityPrototype>()
  91. .Where(p => !p.Abstract)
  92. .Where(p => !pair.IsTestPrototype(p))
  93. .Where(p => p.Components.ContainsKey("StaticPrice"))
  94. .ToList();
  95. foreach (var proto in protoIds)
  96. {
  97. // Sanity check
  98. Assert.That(proto.TryGetComponent<StaticPriceComponent>(out var staticPriceComp, compFact), Is.True);
  99. if (proto.TryGetComponent<StackPriceComponent>(out var stackPriceComp, compFact) && stackPriceComp.Price > 0)
  100. {
  101. Assert.That(staticPriceComp.Price, Is.EqualTo(0),
  102. $"The prototype {proto} has a StackPriceComponent and StaticPriceComponent whose values are not compatible with each other.");
  103. }
  104. if (proto.HasComponent<StackComponent>(compFact))
  105. {
  106. Assert.That(staticPriceComp.Price, Is.EqualTo(0),
  107. $"The prototype {proto} has a StackComponent and StaticPriceComponent whose values are not compatible with each other.");
  108. }
  109. }
  110. });
  111. await pair.CleanReturnAsync();
  112. }
  113. /// <summary>
  114. /// Tests to see if any items that are valid for cargo bounties can be sliced into items that
  115. /// are also valid for the same bounty entry.
  116. /// </summary>
  117. [Test]
  118. public async Task NoSliceableBountyArbitrageTest()
  119. {
  120. await using var pair = await PoolManager.GetServerClient();
  121. var server = pair.Server;
  122. var testMap = await pair.CreateTestMap();
  123. var entManager = server.ResolveDependency<IEntityManager>();
  124. var mapSystem = server.System<SharedMapSystem>();
  125. var mapManager = server.ResolveDependency<IMapManager>();
  126. var protoManager = server.ResolveDependency<IPrototypeManager>();
  127. var componentFactory = server.ResolveDependency<IComponentFactory>();
  128. var whitelist = entManager.System<EntityWhitelistSystem>();
  129. var cargo = entManager.System<CargoSystem>();
  130. var sliceableSys = entManager.System<SliceableFoodSystem>();
  131. var bounties = protoManager.EnumeratePrototypes<CargoBountyPrototype>().ToList();
  132. await server.WaitAssertion(() =>
  133. {
  134. var mapId = testMap.MapId;
  135. var grid = mapManager.CreateGridEntity(mapId);
  136. var coord = new EntityCoordinates(grid.Owner, 0, 0);
  137. var sliceableEntityProtos = protoManager.EnumeratePrototypes<EntityPrototype>()
  138. .Where(p => !p.Abstract)
  139. .Where(p => !pair.IsTestPrototype(p))
  140. .Where(p => p.TryGetComponent<SliceableFoodComponent>(out _, componentFactory))
  141. .Select(p => p.ID)
  142. .ToList();
  143. foreach (var proto in sliceableEntityProtos)
  144. {
  145. var ent = entManager.SpawnEntity(proto, coord);
  146. var sliceable = entManager.GetComponent<SliceableFoodComponent>(ent);
  147. // Check each bounty
  148. foreach (var bounty in bounties)
  149. {
  150. // Check each entry in the bounty
  151. foreach (var entry in bounty.Entries)
  152. {
  153. // See if the entity counts as part of this bounty entry
  154. if (!cargo.IsValidBountyEntry(ent, entry))
  155. continue;
  156. // Spawn a slice
  157. var slice = entManager.SpawnEntity(sliceable.Slice, coord);
  158. // See if the slice also counts for this bounty entry
  159. if (!cargo.IsValidBountyEntry(slice, entry))
  160. {
  161. entManager.DeleteEntity(slice);
  162. continue;
  163. }
  164. entManager.DeleteEntity(slice);
  165. // If for some reason it can only make one slice, that's okay, I guess
  166. Assert.That(sliceable.TotalCount, Is.EqualTo(1), $"{proto} counts as part of cargo bounty {bounty.ID} and slices into {sliceable.TotalCount} slices which count for the same bounty!");
  167. }
  168. }
  169. entManager.DeleteEntity(ent);
  170. }
  171. mapSystem.DeleteMap(mapId);
  172. });
  173. await pair.CleanReturnAsync();
  174. }
  175. [TestPrototypes]
  176. private const string StackProto = @"
  177. - type: entity
  178. id: A
  179. - type: stack
  180. id: StackProto
  181. spawn: A
  182. - type: entity
  183. id: StackEnt
  184. components:
  185. - type: StackPrice
  186. price: 20
  187. - type: Stack
  188. stackType: StackProto
  189. count: 5
  190. ";
  191. [Test]
  192. public async Task StackPrice()
  193. {
  194. await using var pair = await PoolManager.GetServerClient();
  195. var server = pair.Server;
  196. var entManager = server.ResolveDependency<IEntityManager>();
  197. await server.WaitAssertion(() =>
  198. {
  199. var priceSystem = entManager.System<PricingSystem>();
  200. var ent = entManager.SpawnEntity("StackEnt", MapCoordinates.Nullspace);
  201. var price = priceSystem.GetPrice(ent);
  202. Assert.That(price, Is.EqualTo(100.0));
  203. });
  204. await pair.CleanReturnAsync();
  205. }
  206. }