Files
tbd-station-14/Content.Server/Materials/MaterialStorageSystem.cs
Pieter-Jan Briers 3e078ab3e0 Fix error log when recycling something with small material counts. (#32723)
* Fix error log when recycling something with small material counts.

MaterialStorageSystem.SpawnMultipleFromMaterial now doesn't call StackSystem.SpawnMultiple if it tries to spawn zero. This happens because the recycler calls SpawnMultipleFromMaterial for everything recycled, even if the amount it has stored is < the amount for one sheet.

* Update Content.Server/Materials/MaterialStorageSystem.cs

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
2024-10-10 03:29:26 +02:00

244 lines
9.8 KiB
C#

using System.Linq;
using Content.Server.Administration.Logs;
using Content.Shared.Materials;
using Content.Shared.Popups;
using Content.Shared.Stacks;
using Content.Server.Power.Components;
using Content.Server.Stack;
using Content.Shared.ActionBlocker;
using Content.Shared.Construction;
using Content.Shared.Database;
using JetBrains.Annotations;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Server.Materials;
/// <summary>
/// This handles <see cref="SharedMaterialStorageSystem"/>
/// </summary>
public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
{
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly StackSystem _stackSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MaterialStorageComponent, MachineDeconstructedEvent>(OnDeconstructed);
SubscribeAllEvent<EjectMaterialMessage>(OnEjectMessage);
}
private void OnDeconstructed(EntityUid uid, MaterialStorageComponent component, MachineDeconstructedEvent args)
{
if (!component.DropOnDeconstruct)
return;
foreach (var (material, amount) in component.Storage)
{
SpawnMultipleFromMaterial(amount, material, Transform(uid).Coordinates);
}
}
private void OnEjectMessage(EjectMaterialMessage msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not { } player)
return;
var uid = GetEntity(msg.Entity);
if (!TryComp<MaterialStorageComponent>(uid, out var component))
return;
if (!Exists(uid))
return;
if (!_actionBlocker.CanInteract(player, uid))
return;
if (!component.CanEjectStoredMaterials || !_prototypeManager.TryIndex<MaterialPrototype>(msg.Material, out var material))
return;
var volume = 0;
if (material.StackEntity != null)
{
if (!_prototypeManager.Index<EntityPrototype>(material.StackEntity).TryGetComponent<PhysicalCompositionComponent>(out var composition))
return;
var volumePerSheet = composition.MaterialComposition.FirstOrDefault(kvp => kvp.Key == msg.Material).Value;
var sheetsToExtract = Math.Min(msg.SheetsToExtract, _stackSystem.GetMaxCount(material.StackEntity));
volume = sheetsToExtract * volumePerSheet;
}
if (volume <= 0 || !TryChangeMaterialAmount(uid, msg.Material, -volume))
return;
var mats = SpawnMultipleFromMaterial(volume, material, Transform(uid).Coordinates, out _);
foreach (var mat in mats.Where(mat => !TerminatingOrDeleted(mat)))
{
_stackSystem.TryMergeToContacts(mat);
}
}
public override bool TryInsertMaterialEntity(EntityUid user,
EntityUid toInsert,
EntityUid receiver,
MaterialStorageComponent? storage = null,
MaterialComponent? material = null,
PhysicalCompositionComponent? composition = null)
{
if (!Resolve(receiver, ref storage) || !Resolve(toInsert, ref material, ref composition, false))
return false;
if (TryComp<ApcPowerReceiverComponent>(receiver, out var power) && !power.Powered)
return false;
if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, material, composition))
return false;
_audio.PlayPvs(storage.InsertingSound, receiver);
_popup.PopupEntity(Loc.GetString("machine-insert-item", ("user", user), ("machine", receiver),
("item", toInsert)), receiver);
QueueDel(toInsert);
// Logging
TryComp<StackComponent>(toInsert, out var stack);
var count = stack?.Count ?? 1;
_adminLogger.Add(LogType.Action, LogImpact.Low,
$"{ToPrettyString(user):player} inserted {count} {ToPrettyString(toInsert):inserted} into {ToPrettyString(receiver):receiver}");
return true;
}
/// <summary>
/// Spawn an amount of a material in stack entities.
/// Note the 'amount' is material dependent.
/// 1 biomass = 1 biomass in its stack,
/// but 100 plasma = 1 sheet of plasma, etc.
/// </summary>
public List<EntityUid> SpawnMultipleFromMaterial(int amount, string material, EntityCoordinates coordinates)
{
return SpawnMultipleFromMaterial(amount, material, coordinates, out _);
}
/// <summary>
/// Spawn an amount of a material in stack entities.
/// Note the 'amount' is material dependent.
/// 1 biomass = 1 biomass in its stack,
/// but 100 plasma = 1 sheet of plasma, etc.
/// </summary>
public List<EntityUid> SpawnMultipleFromMaterial(int amount, string material, EntityCoordinates coordinates, out int overflowMaterial)
{
overflowMaterial = 0;
if (!_prototypeManager.TryIndex<MaterialPrototype>(material, out var stackType))
{
Log.Error("Failed to index material prototype " + material);
return new List<EntityUid>();
}
return SpawnMultipleFromMaterial(amount, stackType, coordinates, out overflowMaterial);
}
/// <summary>
/// Spawn an amount of a material in stack entities.
/// Note the 'amount' is material dependent.
/// 1 biomass = 1 biomass in its stack,
/// but 100 plasma = 1 sheet of plasma, etc.
/// </summary>
[PublicAPI]
public List<EntityUid> SpawnMultipleFromMaterial(int amount, MaterialPrototype materialProto, EntityCoordinates coordinates)
{
return SpawnMultipleFromMaterial(amount, materialProto, coordinates, out _);
}
/// <summary>
/// Spawn an amount of a material in stack entities.
/// Note the 'amount' is material dependent.
/// 1 biomass = 1 biomass in its stack,
/// but 100 plasma = 1 sheet of plasma, etc.
/// </summary>
public List<EntityUid> SpawnMultipleFromMaterial(int amount, MaterialPrototype materialProto, EntityCoordinates coordinates, out int overflowMaterial)
{
overflowMaterial = 0;
if (amount <= 0 || materialProto.StackEntity == null)
return new List<EntityUid>();
var entProto = _prototypeManager.Index<EntityPrototype>(materialProto.StackEntity);
if (!entProto.TryGetComponent<PhysicalCompositionComponent>(out var composition))
return new List<EntityUid>();
var materialPerStack = composition.MaterialComposition[materialProto.ID];
var amountToSpawn = amount / materialPerStack;
overflowMaterial = amount - amountToSpawn * materialPerStack;
if (amountToSpawn == 0)
return new List<EntityUid>();
return _stackSystem.SpawnMultiple(materialProto.StackEntity, amountToSpawn, coordinates);
}
/// <summary>
/// Eject a material out of this storage. The internal counts are updated.
/// Material that cannot be ejected stays in storage. (e.g. only have 50 but a sheet needs 100).
/// </summary>
/// <param name="entity">The entity with storage to eject from.</param>
/// <param name="material">The material prototype to eject.</param>
/// <param name="maxAmount">The maximum amount to eject. If not given, as much as possible is ejected.</param>
/// <param name="coordinates">The position where to spawn the created sheets. If not given, they're spawned next to the entity.</param>
/// <param name="component">The storage component on <paramref name="entity"/>. Resolved automatically if not given.</param>
/// <returns>The stack entities that were spawned.</returns>
public List<EntityUid> EjectMaterial(
EntityUid entity,
string material,
int? maxAmount = null,
EntityCoordinates? coordinates = null,
MaterialStorageComponent? component = null)
{
if (!Resolve(entity, ref component))
return new List<EntityUid>();
coordinates ??= Transform(entity).Coordinates;
var amount = GetMaterialAmount(entity, material, component);
if (maxAmount != null)
amount = Math.Min(maxAmount.Value, amount);
var spawned = SpawnMultipleFromMaterial(amount, material, coordinates.Value, out var overflow);
TryChangeMaterialAmount(entity, material, -(amount - overflow), component);
return spawned;
}
/// <summary>
/// Eject all material stored in an entity, with the same mechanics as <see cref="EjectMaterial"/>.
/// </summary>
/// <param name="entity">The entity with storage to eject from.</param>
/// <param name="coordinates">The position where to spawn the created sheets. If not given, they're spawned next to the entity.</param>
/// <param name="component">The storage component on <paramref name="entity"/>. Resolved automatically if not given.</param>
/// <returns>The stack entities that were spawned.</returns>
public List<EntityUid> EjectAllMaterial(
EntityUid entity,
EntityCoordinates? coordinates = null,
MaterialStorageComponent? component = null)
{
if (!Resolve(entity, ref component))
return new List<EntityUid>();
coordinates ??= Transform(entity).Coordinates;
var allSpawned = new List<EntityUid>();
foreach (var material in component.Storage.Keys.ToArray())
{
var spawned = EjectMaterial(entity, material, null, coordinates, component);
allSpawned.AddRange(spawned);
}
return allSpawned;
}
}