Fix CryostasisBeaker grenade exploit by limiting solution temperature (#2987)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: VigersRay <60344369+VigersRay@users.noreply.github.com>
Co-authored-by: Vigers Ray <vigersray@gmail.com>
This commit is contained in:
Copilot 2025-08-24 01:27:00 +03:00 committed by GitHub
parent 020ba143df
commit 5bbcd98f99
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 142 additions and 0 deletions

View file

@ -0,0 +1,84 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
namespace Content.IntegrationTests.Tests._Sunrise.Chemistry;
[TestFixture]
[TestOf(typeof(CryostasisBeakerSystem))]
public sealed class CryostasisBeakerTests
{
[TestPrototypes]
private const string Prototypes = @"
- type: entity
id: TestCryostasisBeaker
components:
- type: SolutionContainerManager
solutions:
beaker:
maxVol: 50
canReact: false
- type: CryostasisBeaker
maxTemperature: 293.15
- type: reagent
id: TestReagent
name: reagent-name-nothing
desc: reagent-desc-nothing
physicalDesc: reagent-physical-desc-nothing
";
[Test]
public async Task CryostasisBeakerPreventsHeating()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var testMap = await pair.CreateTestMap();
await server.WaitPost(() =>
{
var solutionSystem = server.System<SharedSolutionContainerSystem>();
var beaker = server.EntMan.SpawnEntity("TestCryostasisBeaker", testMap.GridCoords);
Assert.That(solutionSystem.TryGetSolution(beaker, "beaker", out var solutionEntity, out var solution));
solutionSystem.TryAddReagent(solutionEntity.Value, "TestReagent", FixedPoint2.New(10));
solutionSystem.SetTemperature(solutionEntity.Value, 500.0f);
Assert.That(solution!.Temperature, Is.LessThanOrEqualTo(293.15f));
solutionSystem.AddThermalEnergy(solutionEntity.Value, 10000.0f);
Assert.That(solution.Temperature, Is.LessThanOrEqualTo(293.15f));
});
}
[Test]
public async Task NormalBeakerAllowsHeating()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var testMap = await pair.CreateTestMap();
await server.WaitPost(() =>
{
var solutionSystem = server.System<SharedSolutionContainerSystem>();
var beaker = server.EntMan.SpawnEntity("TestCryostasisBeaker", testMap.GridCoords);
if (server.EntMan.HasComponent<CryostasisBeakerComponent>(beaker))
server.EntMan.RemoveComponent<CryostasisBeakerComponent>(beaker);
Assert.That(solutionSystem.TryGetSolution(beaker, "beaker", out var solutionEntity, out var solution));
solutionSystem.TryAddReagent(solutionEntity.Value, "TestReagent", FixedPoint2.New(10));
solutionSystem.SetTemperature(solutionEntity.Value, 500.0f);
Assert.That(solution!.Temperature, Is.EqualTo(500.0f));
});
}
}

View file

@ -0,0 +1,16 @@
namespace Content.Shared.Chemistry.Components;
/// <summary>
/// Component that marks a beaker as a cryostasis beaker, which prevents solutions inside from being heated.
/// Solutions in cryostasis beakers will remain at or below room temperature (293.15K).
/// </summary>
[RegisterComponent]
public sealed partial class CryostasisBeakerComponent : Component
{
/// <summary>
/// Maximum temperature that solutions in this beaker can reach.
/// Default is room temperature (293.15K).
/// </summary>
[DataField("maxTemperature")]
public float MaxTemperature = 293.15f;
}

View file

@ -0,0 +1,38 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Components.SolutionManager;
namespace Content.Shared.Chemistry.EntitySystems;
/// <summary>
/// System that prevents solutions in cryostasis beakers from being heated above room temperature.
/// </summary>
public sealed class CryostasisBeakerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SolutionComponent, SolutionChangedEvent>(OnSolutionChanged);
}
private void OnSolutionChanged(EntityUid solutionEntity, SolutionComponent solutionComp, ref SolutionChangedEvent args)
{
// Get the container that holds this solution
if (!TryComp<ContainedSolutionComponent>(solutionEntity, out var containedSolution))
return;
var containerEntity = containedSolution.Container;
// Check if the container is a CryostasisBeaker
if (!TryComp<CryostasisBeakerComponent>(containerEntity, out var cryostasisBeaker))
return;
var solution = solutionComp.Solution;
// If the solution temperature is above the maximum allowed, cool it down
if (solution.Temperature > cryostasisBeaker.MaxTemperature)
{
solution.Temperature = cryostasisBeaker.MaxTemperature;
}
}
}

View file

@ -245,6 +245,10 @@
beaker:
maxVol: 60
canReact: false
# Sunrise-Start
- type: CryostasisBeaker
maxTemperature: 293.15
# Sunrise-End
- type: entity
name: bluespace beaker