Material Reclaimer (#14969)
* Material Reclaimer * Fix this test * autostack output, tweak volume, add upgrade examine * whitelist AND blacklist support why not * trying so hard to get this fucking test to work * EmoGarbage delves into MaterialArbitrageTest, never to return * VV and restore cloth to glory * make the system more robust * even more stuff has composition; add blacklist for important items * fix test fails * convert recycling * forgor :sadge: * lol * simply a modiCUM of doc commentary
This commit is contained in:
@@ -14,10 +14,12 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Materials;
|
||||
|
||||
namespace Content.IntegrationTests.Tests;
|
||||
|
||||
@@ -31,10 +33,7 @@ public sealed class MaterialArbitrageTest
|
||||
[Test]
|
||||
public async Task NoMaterialArbitrage()
|
||||
{
|
||||
// TODO check lathe resource prices?
|
||||
// I CBF doing that atm because I know that will probably fail for most lathe recipies.
|
||||
|
||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings() {NoClient = true});
|
||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings {NoClient = true});
|
||||
var server = pairTracker.Pair.Server;
|
||||
|
||||
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
||||
@@ -51,8 +50,9 @@ public sealed class MaterialArbitrageTest
|
||||
var compFact = server.ResolveDependency<IComponentFactory>();
|
||||
|
||||
var constructionName = compFact.GetComponentName(typeof(ConstructionComponent));
|
||||
var compositionName = compFact.GetComponentName(typeof(PhysicalCompositionComponent));
|
||||
var materialName = compFact.GetComponentName(typeof(MaterialComponent));
|
||||
var destructibleName = compFact.GetComponentName(typeof(DestructibleComponent));
|
||||
var stackName = compFact.GetComponentName(typeof(StackComponent));
|
||||
|
||||
// construct inverted lathe recipe dictionary
|
||||
Dictionary<string, LatheRecipePrototype> latheRecipes = new();
|
||||
@@ -84,6 +84,9 @@ public sealed class MaterialArbitrageTest
|
||||
{
|
||||
var materials = new Dictionary<string, int>();
|
||||
var graph = protoManager.Index<ConstructionGraphPrototype>(comp.Graph);
|
||||
if (graph.Start == null)
|
||||
continue;
|
||||
|
||||
if (!graph.TryPath(graph.Start, comp.Node, out var path) || path.Length == 0)
|
||||
continue;
|
||||
|
||||
@@ -93,10 +96,25 @@ public sealed class MaterialArbitrageTest
|
||||
var edge = cur.GetEdge(node.Name);
|
||||
cur = node;
|
||||
|
||||
if (edge == null)
|
||||
continue;
|
||||
|
||||
foreach (var step in edge.Steps)
|
||||
{
|
||||
if (step is MaterialConstructionGraphStep materialStep)
|
||||
materials[materialStep.MaterialPrototypeId] = materialStep.Amount + materials.GetValueOrDefault(materialStep.MaterialPrototypeId);
|
||||
if (step is not MaterialConstructionGraphStep materialStep)
|
||||
continue;
|
||||
|
||||
var stackProto = protoManager.Index<StackPrototype>(materialStep.MaterialPrototypeId);
|
||||
var spawnProto = protoManager.Index<EntityPrototype>(stackProto.Spawn);
|
||||
|
||||
if (!spawnProto.Components.TryGetValue(materialName, out var matreg))
|
||||
continue;
|
||||
|
||||
var mat = (MaterialComponent) matreg.Component;
|
||||
foreach (var (matId, amount) in mat.Materials)
|
||||
{
|
||||
materials[matId] = materialStep.Amount * amount + materials.GetValueOrDefault(matId);
|
||||
}
|
||||
}
|
||||
}
|
||||
constructionMaterials.Add(id, materials);
|
||||
@@ -136,11 +154,16 @@ public sealed class MaterialArbitrageTest
|
||||
spawnedEnts[key] = spawnedEnts.GetValueOrDefault(key) + value.Max;
|
||||
|
||||
var spawnProto = protoManager.Index<EntityPrototype>(key);
|
||||
if (!spawnProto.Components.TryGetValue(stackName, out var reg))
|
||||
continue;
|
||||
|
||||
var stack = (StackComponent) reg.Component;
|
||||
spawnedMats[stack.StackTypeId] = value.Max + spawnedMats.GetValueOrDefault(stack.StackTypeId);
|
||||
// get the amount of each material included in the entity
|
||||
if (!spawnProto.Components.TryGetValue(materialName, out var matreg))
|
||||
continue;
|
||||
var mat = (MaterialComponent) matreg.Component;
|
||||
|
||||
foreach (var (matId, amount) in mat.Materials)
|
||||
{
|
||||
spawnedMats[matId] = value.Max * amount + spawnedMats.GetValueOrDefault(matId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,18 +234,21 @@ public sealed class MaterialArbitrageTest
|
||||
continue;
|
||||
|
||||
var spawnProto = protoManager.Index<EntityPrototype>(spawnCompletion.Prototype);
|
||||
if (!spawnProto.Components.TryGetValue(stackName, out var reg))
|
||||
|
||||
if (!spawnProto.Components.TryGetValue(materialName, out var matreg))
|
||||
continue;
|
||||
|
||||
var stack = (StackComponent) reg.Component;
|
||||
|
||||
materials[stack.StackTypeId] = spawnCompletion.Amount + materials.GetValueOrDefault(stack.StackTypeId);
|
||||
var mat = (MaterialComponent) matreg.Component;
|
||||
foreach (var (matId, amount) in mat.Materials)
|
||||
{
|
||||
materials[matId] = spawnCompletion.Amount * amount + materials.GetValueOrDefault(matId);
|
||||
}
|
||||
}
|
||||
}
|
||||
deconstructionMaterials.Add(id, materials);
|
||||
}
|
||||
|
||||
// This is functionally the same loop as before, but now testinng deconstruction rather than destruction.
|
||||
// This is functionally the same loop as before, but now testing deconstruction rather than destruction.
|
||||
// This is pretty braindead. In principle construction graphs can have loops and whatnot.
|
||||
|
||||
Assert.Multiple(async () =>
|
||||
@@ -258,6 +284,58 @@ public sealed class MaterialArbitrageTest
|
||||
}
|
||||
});
|
||||
|
||||
// create phyiscal composition dictionary
|
||||
// this doesn't account for the chemicals in the composition
|
||||
Dictionary<string, PhysicalCompositionComponent> physicalCompositions = new();
|
||||
foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (proto.NoSpawn || proto.Abstract)
|
||||
continue;
|
||||
|
||||
if (!proto.Components.TryGetValue(compositionName, out var composition))
|
||||
continue;
|
||||
|
||||
var comp = (PhysicalCompositionComponent) composition.Component;
|
||||
physicalCompositions.Add(proto.ID, comp);
|
||||
}
|
||||
|
||||
// This is functionally the same loop as before, but now testing composition rather than destruction or deconstruction.
|
||||
// This doesn't take into account chemicals generated when deconstructing. Maybe it should.
|
||||
Assert.Multiple(async () =>
|
||||
{
|
||||
foreach (var (id, compositionComponent) in physicalCompositions)
|
||||
{
|
||||
// Check cargo sell price
|
||||
var materialPrice = await GetDeconstructedPrice(compositionComponent.MaterialComposition);
|
||||
var chemicalPrice = await GetChemicalCompositionPrice(compositionComponent.ChemicalComposition);
|
||||
var sumPrice = materialPrice + chemicalPrice;
|
||||
var price = await GetPrice(id);
|
||||
if (sumPrice > 0 && price > 0)
|
||||
Assert.LessOrEqual(sumPrice, price, $"{id} increases in price after decomposed into raw materials");
|
||||
|
||||
// Check lathe production
|
||||
if (latheRecipes.TryGetValue(id, out var recipe))
|
||||
{
|
||||
foreach (var (matId, amount) in recipe.RequiredMaterials)
|
||||
{
|
||||
var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier);
|
||||
if (compositionComponent.MaterialComposition.TryGetValue(matId, out var numSpawned))
|
||||
Assert.LessOrEqual(numSpawned, actualAmount, $"The physical composition of {id} has more {matId} than required to produce via an (upgraded) lathe.");
|
||||
}
|
||||
}
|
||||
|
||||
// Check construction.
|
||||
if (constructionMaterials.TryGetValue(id, out var constructionMats))
|
||||
{
|
||||
foreach (var (matId, amount) in constructionMats)
|
||||
{
|
||||
if (compositionComponent.MaterialComposition.TryGetValue(matId, out var numSpawned))
|
||||
Assert.LessOrEqual(numSpawned, amount, $"The physical composition of {id} has more {matId} than required to construct it.");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await server.WaitPost(() => mapManager.DeleteMap(testMap.MapId));
|
||||
await pairTracker.CleanReturnAsync();
|
||||
|
||||
@@ -293,8 +371,20 @@ public sealed class MaterialArbitrageTest
|
||||
double price = 0;
|
||||
foreach (var (id, num) in mats)
|
||||
{
|
||||
var matProto = protoManager.Index<StackPrototype>(id).Spawn;
|
||||
price += num * await GetPrice(matProto);
|
||||
var matProto = protoManager.Index<MaterialPrototype>(id);
|
||||
price += num * matProto.Price;
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
|
||||
async Task<double> GetChemicalCompositionPrice(Dictionary<string, FixedPoint2> mats)
|
||||
{
|
||||
double price = 0;
|
||||
foreach (var (id, num) in mats)
|
||||
{
|
||||
var reagentProto = protoManager.Index<ReagentPrototype>(id);
|
||||
price += num.Double() * reagentProto.PricePerUnit;
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user