Add a test for sliceable cargo bounty exploits (#28357)
This commit is contained in:
@@ -3,8 +3,13 @@ using System.Linq;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Server.Cargo.Components;
|
using Content.Server.Cargo.Components;
|
||||||
using Content.Server.Cargo.Systems;
|
using Content.Server.Cargo.Systems;
|
||||||
|
using Content.Server.Nutrition.Components;
|
||||||
|
using Content.Server.Nutrition.EntitySystems;
|
||||||
using Content.Shared.Cargo.Prototypes;
|
using Content.Shared.Cargo.Prototypes;
|
||||||
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
|
using Content.Shared.Tag;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -149,6 +154,80 @@ public sealed class CargoTest
|
|||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests to see if any items that are valid for cargo bounties can be sliced into items that
|
||||||
|
/// are also valid for the same bounty entry.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task NoSliceableBountyArbitrageTest()
|
||||||
|
{
|
||||||
|
await using var pair = await PoolManager.GetServerClient();
|
||||||
|
var server = pair.Server;
|
||||||
|
|
||||||
|
var testMap = await pair.CreateTestMap();
|
||||||
|
|
||||||
|
var entManager = server.ResolveDependency<IEntityManager>();
|
||||||
|
var mapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
|
var componentFactory = server.ResolveDependency<IComponentFactory>();
|
||||||
|
var whitelist = entManager.System<EntityWhitelistSystem>();
|
||||||
|
var cargo = entManager.System<CargoSystem>();
|
||||||
|
var sliceableSys = entManager.System<SliceableFoodSystem>();
|
||||||
|
|
||||||
|
var bounties = protoManager.EnumeratePrototypes<CargoBountyPrototype>().ToList();
|
||||||
|
|
||||||
|
await server.WaitAssertion(() =>
|
||||||
|
{
|
||||||
|
var mapId = testMap.MapId;
|
||||||
|
var grid = mapManager.CreateGridEntity(mapId);
|
||||||
|
var coord = new EntityCoordinates(grid.Owner, 0, 0);
|
||||||
|
|
||||||
|
var sliceableEntityProtos = protoManager.EnumeratePrototypes<EntityPrototype>()
|
||||||
|
.Where(p => !p.Abstract)
|
||||||
|
.Where(p => !pair.IsTestPrototype(p))
|
||||||
|
.Where(p => p.TryGetComponent<SliceableFoodComponent>(out _, componentFactory))
|
||||||
|
.Select(p => p.ID)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var proto in sliceableEntityProtos)
|
||||||
|
{
|
||||||
|
var ent = entManager.SpawnEntity(proto, coord);
|
||||||
|
var sliceable = entManager.GetComponent<SliceableFoodComponent>(ent);
|
||||||
|
|
||||||
|
// Check each bounty
|
||||||
|
foreach (var bounty in bounties)
|
||||||
|
{
|
||||||
|
// Check each entry in the bounty
|
||||||
|
foreach (var entry in bounty.Entries)
|
||||||
|
{
|
||||||
|
// See if the entity counts as part of this bounty entry
|
||||||
|
if (!cargo.IsValidBountyEntry(ent, entry))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Spawn a slice
|
||||||
|
var slice = entManager.SpawnEntity(sliceable.Slice, coord);
|
||||||
|
|
||||||
|
// See if the slice also counts for this bounty entry
|
||||||
|
if (!cargo.IsValidBountyEntry(slice, entry))
|
||||||
|
{
|
||||||
|
entManager.DeleteEntity(slice);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entManager.DeleteEntity(slice);
|
||||||
|
|
||||||
|
// If for some reason it can only make one slice, that's okay, I guess
|
||||||
|
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!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entManager.DeleteEntity(ent);
|
||||||
|
}
|
||||||
|
mapManager.DeleteMap(mapId);
|
||||||
|
});
|
||||||
|
|
||||||
|
await pair.CleanReturnAsync();
|
||||||
|
}
|
||||||
|
|
||||||
[TestPrototypes]
|
[TestPrototypes]
|
||||||
private const string StackProto = @"
|
private const string StackProto = @"
|
||||||
|
|||||||
@@ -300,6 +300,21 @@ public sealed partial class CargoSystem
|
|||||||
return IsBountyComplete(GetBountyEntities(container), entries, out bountyEntities);
|
return IsBountyComplete(GetBountyEntities(container), entries, out bountyEntities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the <paramref name="entity"/> meets the criteria for the bounty <paramref name="entry"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if <paramref name="entity"/> is a valid item for the bounty entry, otherwise false</returns>
|
||||||
|
public bool IsValidBountyEntry(EntityUid entity, CargoBountyItemEntry entry)
|
||||||
|
{
|
||||||
|
if (!_whitelistSys.IsValid(entry.Whitelist, entity))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (entry.Blacklist != null && _whitelistSys.IsValid(entry.Blacklist, entity))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsBountyComplete(HashSet<EntityUid> entities, IEnumerable<CargoBountyItemEntry> entries, out HashSet<EntityUid> bountyEntities)
|
public bool IsBountyComplete(HashSet<EntityUid> entities, IEnumerable<CargoBountyItemEntry> entries, out HashSet<EntityUid> bountyEntities)
|
||||||
{
|
{
|
||||||
bountyEntities = new();
|
bountyEntities = new();
|
||||||
@@ -313,7 +328,7 @@ public sealed partial class CargoSystem
|
|||||||
var temp = new HashSet<EntityUid>();
|
var temp = new HashSet<EntityUid>();
|
||||||
foreach (var entity in entities)
|
foreach (var entity in entities)
|
||||||
{
|
{
|
||||||
if (!_whitelistSys.IsValid(entry.Whitelist, entity) || (entry.Blacklist != null && _whitelistSys.IsValid(entry.Blacklist, entity)))
|
if (!IsValidBountyEntry(entity, entry))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
count += _stackQuery.CompOrNull(entity)?.Count ?? 1;
|
count += _stackQuery.CompOrNull(entity)?.Count ?? 1;
|
||||||
|
|||||||
@@ -42,6 +42,9 @@
|
|||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Bread
|
- Bread
|
||||||
|
blacklist:
|
||||||
|
tags:
|
||||||
|
- Slice
|
||||||
|
|
||||||
- type: cargoBounty
|
- type: cargoBounty
|
||||||
id: BountyCarrot
|
id: BountyCarrot
|
||||||
@@ -533,6 +536,9 @@
|
|||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Meat
|
- Meat
|
||||||
|
blacklist:
|
||||||
|
components:
|
||||||
|
- SliceableFood
|
||||||
|
|
||||||
- type: cargoBounty
|
- type: cargoBounty
|
||||||
id: BountyFruit
|
id: BountyFruit
|
||||||
|
|||||||
Reference in New Issue
Block a user