From d2ac15c76f714144b6ffc583f87b3b097610fb0f Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Thu, 15 Jan 2026 19:22:24 -0500 Subject: [PATCH] Fix flatpacker exploit ignoring board costs (#42445) Fix flatpacks ignoring costs and board requirements --- .../UI/FlatpackCreatorMenu.xaml.cs | 57 ++++++++----------- Content.Server/Construction/FlatpackSystem.cs | 32 ++--------- .../Construction/MachinePartSystem.cs | 19 +++++-- .../Construction/SharedFlatpackSystem.cs | 31 ++++++++-- .../construction/components/flatpack.ftl | 2 + 5 files changed, 70 insertions(+), 71 deletions(-) diff --git a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs index 8ee8df48fd..7db15596bd 100644 --- a/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs +++ b/Content.Client/Construction/UI/FlatpackCreatorMenu.xaml.cs @@ -1,13 +1,11 @@ using System.Linq; using Content.Client.Materials; -using Content.Client.Materials.UI; using Content.Client.Message; using Content.Client.UserInterface.Controls; using Content.Shared.Construction.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Materials; using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -61,57 +59,48 @@ public sealed partial class FlatpackCreatorMenu : FancyWindow !_itemSlots.TryGetSlot(_owner, flatpacker.SlotId, out var itemSlot)) return; + var flatpackerEnt = (_owner, flatpacker); + if (flatpacker.Packing) { PackButton.Disabled = true; } else if (_currentBoard != null) { - Dictionary cost; - if (_entityManager.TryGetComponent(_currentBoard, out var machineBoardComp)) - cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, machineBoardComp)); - else - cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), null); - - PackButton.Disabled = !_materialStorage.CanChangeMaterialAmount(_owner, cost); + PackButton.Disabled = !_flatpack.TryGetFlatpackCreationCost(flatpackerEnt, _currentBoard.Value, out var curCost) + || !_materialStorage.CanChangeMaterialAmount(_owner, curCost); } if (_currentBoard == itemSlot.Item) return; _currentBoard = itemSlot.Item; - CostHeaderLabel.Visible = _currentBoard != null; + CostHeaderLabel.Visible = false; InsertLabel.Visible = _currentBoard == null; - if (_currentBoard is not null) + if (_currentBoard is null) + { + MachineSprite.SetPrototype(NoBoardEffectId); + CostLabel.SetMessage(Loc.GetString("flatpacker-ui-no-board-label")); + MachineNameLabel.SetMessage(string.Empty); + PackButton.Disabled = true; + return; + } + + if (_flatpack.TryGetFlatpackResultPrototype(_currentBoard.Value, out var prototype) && + _flatpack.TryGetFlatpackCreationCost(flatpackerEnt, _currentBoard.Value, out var cost)) { - string? prototype = null; - Dictionary? cost = null; - - if (_entityManager.TryGetComponent(_currentBoard, out var newMachineBoardComp)) - { - prototype = newMachineBoardComp.Prototype; - cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), (_currentBoard.Value, newMachineBoardComp)); - } - else if (_entityManager.TryGetComponent(_currentBoard, out var computerBoard)) - { - prototype = computerBoard.Prototype; - cost = _flatpack.GetFlatpackCreationCost((_owner, flatpacker), null); - } - - if (prototype is not null && cost is not null) - { - var proto = _prototypeManager.Index(prototype); - MachineSprite.SetPrototype(prototype); - MachineNameLabel.SetMessage(proto.Name); - CostLabel.SetMarkup(GetCostString(cost)); - } + var proto = _prototypeManager.Index(prototype); + MachineSprite.SetPrototype(prototype); + MachineNameLabel.SetMessage(proto.Name); + CostLabel.SetMarkup(GetCostString(cost)); + CostHeaderLabel.Visible = true; } else { MachineSprite.SetPrototype(NoBoardEffectId); - CostLabel.SetMessage(Loc.GetString("flatpacker-ui-no-board-label")); - MachineNameLabel.SetMessage(" "); + CostLabel.SetMarkup(Loc.GetString("flatpacker-ui-board-invalid-label")); + MachineNameLabel.SetMessage(string.Empty); PackButton.Disabled = true; } } diff --git a/Content.Server/Construction/FlatpackSystem.cs b/Content.Server/Construction/FlatpackSystem.cs index 11303e7f2e..baa123a928 100644 --- a/Content.Server/Construction/FlatpackSystem.cs +++ b/Content.Server/Construction/FlatpackSystem.cs @@ -1,11 +1,9 @@ using Content.Server.Audio; -using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Construction; using Content.Shared.Construction.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Power; -using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Server.Construction; @@ -35,16 +33,8 @@ public sealed class FlatpackSystem : SharedFlatpackSystem if (!_itemSlots.TryGetSlot(uid, comp.SlotId, out var itemSlot) || itemSlot.Item is not { } board) return; - Dictionary cost; - if (TryComp(board, out var machine)) - cost = GetFlatpackCreationCost(ent, (board, machine)); - else if (TryComp(board, out var computer) && computer.Prototype != null) - cost = GetFlatpackCreationCost(ent, null); - else - { - Log.Error($"Encountered invalid flatpack board while packing: {ToPrettyString(board)}"); + if (!TryGetFlatpackCreationCost(ent, board, out var cost)) return; - } if (!MaterialStorage.CanChangeMaterialAmount(uid, cost)) return; @@ -80,29 +70,15 @@ public sealed class FlatpackSystem : SharedFlatpackSystem if (!_itemSlots.TryGetSlot(uid, comp.SlotId, out var itemSlot) || itemSlot.Item is not { } board) return; - Dictionary cost; - EntProtoId proto; - if (TryComp(board, out var machine)) - { - cost = GetFlatpackCreationCost(ent, (board, machine)); - proto = machine.Prototype; - } - else if (TryComp(board, out var computer) && computer.Prototype != null) - { - cost = GetFlatpackCreationCost(ent, null); - proto = computer.Prototype; - } - else - { - Log.Error($"Encountered invalid flatpack board while packing: {ToPrettyString(board)}"); + if (!TryGetFlatpackCreationCost(ent, board, out var cost) || + !TryGetFlatpackResultPrototype(board, out var proto)) return; - } if (!MaterialStorage.TryChangeMaterialAmount((ent, null), cost)) return; var flatpack = Spawn(comp.BaseFlatpackPrototype, Transform(ent).Coordinates); - SetupFlatpack(flatpack, proto, board); + SetupFlatpack(flatpack, proto.Value, board); Del(board); } diff --git a/Content.Shared/Construction/MachinePartSystem.cs b/Content.Shared/Construction/MachinePartSystem.cs index 7ac9fde94c..4783c2a332 100644 --- a/Content.Shared/Construction/MachinePartSystem.cs +++ b/Content.Shared/Construction/MachinePartSystem.cs @@ -58,11 +58,11 @@ namespace Content.Shared.Construction } } - public Dictionary GetMachineBoardMaterialCost(Entity entity, int coefficient = 1) + public bool TryGetMachineBoardMaterialCost(Entity entity, out Dictionary materials, int coefficient = 1) { var (_, comp) = entity; - var materials = new Dictionary(); + materials = new Dictionary(); foreach (var (stackId, amount) in comp.StackRequirements) { @@ -89,9 +89,14 @@ namespace Content.Shared.Construction materials[mat] += matAmount * amount * coefficient; } } + else + { + // The item has no material cost, so we cannot get the full cost. + return false; + } } - var genericPartInfo = comp.ComponentRequirements.Values.Concat(comp.ComponentRequirements.Values); + var genericPartInfo = comp.ComponentRequirements.Values.Concat(comp.TagRequirements.Values); foreach (var info in genericPartInfo) { var amount = info.Amount; @@ -118,9 +123,15 @@ namespace Content.Shared.Construction materials[mat] += matAmount * amount * coefficient; } } + else + { + // The item has no material cost, so we cannot get the full cost. + return false; + } } - return materials; + // We were able to construct all elements of the recipe. + return true; } } } diff --git a/Content.Shared/Construction/SharedFlatpackSystem.cs b/Content.Shared/Construction/SharedFlatpackSystem.cs index a83948b167..f6a56d9ae4 100644 --- a/Content.Shared/Construction/SharedFlatpackSystem.cs +++ b/Content.Shared/Construction/SharedFlatpackSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Shared.Construction.Components; using Content.Shared.Administration.Logs; using Content.Shared.Containers.ItemSlots; @@ -125,14 +126,34 @@ public abstract class SharedFlatpackSystem : EntitySystem Appearance.SetData(ent, FlatpackVisuals.Machine, MetaData(board).EntityPrototype?.ID ?? string.Empty); } + /// + /// Returns the prototype from a board that the flatpacker will create. + /// + public bool TryGetFlatpackResultPrototype(EntityUid board, [NotNullWhen(true)] out EntProtoId? prototype) + { + prototype = null; + + if (TryComp(board, out var machine)) + prototype = machine.Prototype; + else if (TryComp(board, out var computer)) + prototype = computer.Prototype; + return prototype is not null; + } + + /// + /// Tries to get the cost to produce an item, fails if unable to produce it. + /// + /// The flatpacking machine /// The machine board to pack. If null, this implies we are packing a computer board - public Dictionary GetFlatpackCreationCost(Entity entity, Entity? machineBoard) + /// Cost to produce + public bool TryGetFlatpackCreationCost(Entity entity, EntityUid machineBoard, out Dictionary cost) { - Dictionary cost = new(); + cost = new(); Dictionary, int> baseCost; - if (machineBoard is not null) + if (TryComp(machineBoard, out var machineBoardComp)) { - cost = MachinePart.GetMachineBoardMaterialCost(machineBoard.Value, -1); + if (!MachinePart.TryGetMachineBoardMaterialCost((machineBoard, machineBoardComp), out cost, -1)) + return false; baseCost = entity.Comp.BaseMachineCost; } else @@ -144,6 +165,6 @@ public abstract class SharedFlatpackSystem : EntitySystem cost[mat] -= amount; } - return cost; + return true; } } diff --git a/Resources/Locale/en-US/construction/components/flatpack.ftl b/Resources/Locale/en-US/construction/components/flatpack.ftl index 0957d92f3e..a47cea782c 100644 --- a/Resources/Locale/en-US/construction/components/flatpack.ftl +++ b/Resources/Locale/en-US/construction/components/flatpack.ftl @@ -8,5 +8,7 @@ flatpacker-ui-title = Flatpacker 1001 flatpacker-ui-materials-label = Materials flatpacker-ui-cost-label = Packing Cost flatpacker-ui-no-board-label = No board present! +flatpacker-ui-board-invalid-label = [color=red]Invalid board! + Unable to print![/color] flatpacker-ui-insert-board = Insert a board to begin. flatpacker-ui-pack-button = Pack -- 2.52.0