mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
Raise SolutionChangedEvent and SolutionContainerChangedEvent when handling SolutionComponent states (#42814)
raise event on client + comp access
This commit is contained in:
parent
2484ec7f36
commit
210fae0ffe
13 changed files with 105 additions and 22 deletions
|
|
@ -50,7 +50,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
|
|||
beaker = entityManager.SpawnEntity("TestSolutionContainer", coordinates);
|
||||
Assert.That(solutionContainerSystem
|
||||
.TryGetSolution(beaker, "beaker", out solutionEnt, out solution));
|
||||
solutionEnt.Value.Comp.Solution.CanReact = false;
|
||||
solutionContainerSystem.SetCanReact(solutionEnt.Value, false);
|
||||
foreach (var (id, reactant) in reactionPrototype.Reactants)
|
||||
{
|
||||
#pragma warning disable NUnit2045
|
||||
|
|
@ -84,9 +84,8 @@ namespace Content.IntegrationTests.Tests.Chemistry
|
|||
}
|
||||
|
||||
//Now safe set the temperature and mix the reagents
|
||||
solutionEnt.Value.Comp.Solution.CanReact = true;
|
||||
solutionContainerSystem.SetTemperature(solutionEnt.Value, reactionPrototype.MinimumTemperature);
|
||||
solutionContainerSystem.UpdateChemicals(solutionEnt.Value);
|
||||
solutionContainerSystem.SetCanReact(solutionEnt.Value, true);
|
||||
|
||||
if (reactionPrototype.MixingCategories != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -188,6 +188,11 @@ namespace Content.Shared.Chemistry.Components
|
|||
return new Solution(this);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Join("; ", Contents);
|
||||
}
|
||||
|
||||
[AssertionMethod]
|
||||
public void ValidateSolution()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Materials;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components;
|
||||
|
||||
|
|
@ -11,12 +13,20 @@ namespace Content.Shared.Chemistry.Components;
|
|||
/// <remarks>
|
||||
/// <para>Once reagents and materials have been merged this component should be depricated in favor of using a combination of <see cref="PhysicalCompositionComponent"/> and <see cref="Content.Server.Temperature.Components.TemperatureComponent"/>. May require minor reworks to both.</para>
|
||||
/// </remarks>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(SharedSolutionContainerSystem))]
|
||||
public sealed partial class SolutionComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>The reagents the entity is composed of and their temperature.</para>
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[DataField]
|
||||
public Solution Solution = new();
|
||||
}
|
||||
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SolutionComponentState(Solution solution) : ComponentState
|
||||
{
|
||||
public Solution Solution = solution;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
|
|
@ -9,6 +10,7 @@ public sealed class ReactiveContainerSystem : EntitySystem
|
|||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -34,6 +36,10 @@ public sealed class ReactiveContainerSystem : EntitySystem
|
|||
|
||||
private void OnSolutionChange(EntityUid uid, ReactiveContainerComponent comp, SolutionContainerChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(uid, comp.Solution, out _, out var solution))
|
||||
return;
|
||||
if (solution.Volume == 0)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Content.Shared.FixedPoint;
|
|||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
|
|
@ -16,6 +17,7 @@ public sealed class RehydratableSystem : EntitySystem
|
|||
[Dependency] private readonly SharedSolutionContainerSystem _solutions = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -26,6 +28,10 @@ public sealed class RehydratableSystem : EntitySystem
|
|||
|
||||
private void OnSolutionChange(Entity<RehydratableComponent> ent, ref SolutionContainerChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
var quantity = _solutions.GetTotalPrototypeQuantity(ent, ent.Comp.CatalystPrototype);
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner)} was hydrated, now contains a solution of: {SharedSolutionContainerSystem.ToPrettyString(args.Solution)}.");
|
||||
if (quantity != FixedPoint2.Zero && quantity >= ent.Comp.CatalystMinimum)
|
||||
|
|
|
|||
|
|
@ -8,19 +8,20 @@ namespace Content.Shared.Chemistry.EntitySystems;
|
|||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// This event alerts system that the solution was changed
|
||||
/// Raised on the container of the solution entity when the contained solution is changed.
|
||||
/// If you want to subscribe with the solution entity itself
|
||||
/// then use <see cref="SolutionChangedEvent"/> instead.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is always raised on the client when handling the component state so that we can update UIs accordingly.
|
||||
/// You might need an IGameTiming.ApplyingState guard to prevent mispredicts if the changes from your subscription are
|
||||
/// networked with the same game state.
|
||||
/// </remarks>
|
||||
[ByRefEvent]
|
||||
public record struct SolutionContainerChangedEvent
|
||||
public record struct SolutionContainerChangedEvent(Solution Solution, string SolutionId)
|
||||
{
|
||||
public readonly Solution Solution;
|
||||
public readonly string SolutionId;
|
||||
|
||||
public SolutionContainerChangedEvent(Solution solution, string solutionId)
|
||||
{
|
||||
SolutionId = solutionId;
|
||||
Solution = solution;
|
||||
}
|
||||
public readonly Solution Solution = Solution;
|
||||
public readonly string SolutionId = SolutionId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -89,10 +90,14 @@ public abstract partial class SharedSolutionContainerSystem
|
|||
var (solutionId, solutionComp) = args.Solution;
|
||||
var solution = solutionComp.Solution;
|
||||
|
||||
UpdateAppearance(entity.Comp.Container, (solutionId, solutionComp, entity.Comp));
|
||||
|
||||
var relayEvent = new SolutionContainerChangedEvent(solution, entity.Comp.ContainerName);
|
||||
RaiseLocalEvent(entity.Comp.Container, ref relayEvent);
|
||||
|
||||
// The appearance changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
UpdateAppearance(entity.Comp.Container, (solutionId, solutionComp, entity.Comp));
|
||||
}
|
||||
|
||||
protected virtual void OnSolutionOverflow(Entity<ContainedSolutionComponent> entity, ref SolutionOverflowEvent args)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
|
|
@ -10,17 +9,17 @@ using Content.Shared.Chemistry.Reagent;
|
|||
using Content.Shared.Containers;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Localizations;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Dependency = Robust.Shared.IoC.DependencyAttribute;
|
||||
|
||||
|
|
@ -28,9 +27,15 @@ namespace Content.Shared.Chemistry.EntitySystems;
|
|||
|
||||
/// <summary>
|
||||
/// The event raised whenever a solution entity is modified.
|
||||
/// Raised on the solution entity itself.
|
||||
/// If you want to subscribe with the entity containing the solution entity
|
||||
/// then use <see cref="SolutionContainerChangedEvent"/> instead.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Raised after chemcial reactions and <see cref="SolutionOverflowEvent"/> are handled.
|
||||
/// This is always raised on the client when handling the component state so that we can update UIs accordingly.
|
||||
/// You might need an IGameTiming.ApplyingState guard to prevent mispredicts if the changes from your subscription are
|
||||
/// networked with the same game state.
|
||||
/// </remarks>
|
||||
/// <param name="Solution">The solution entity that has been modified.</param>
|
||||
[ByRefEvent]
|
||||
|
|
@ -73,6 +78,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
|||
[Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
|
||||
[Dependency] protected readonly MetaDataSystem MetaDataSys = default!;
|
||||
[Dependency] protected readonly INetManager NetManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -80,6 +86,8 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
|||
|
||||
InitializeRelays();
|
||||
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentGetState>(OnSolutionGetState);
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentHandleState>(OnSolutionHandleState);
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentStartup>(OnSolutionStartup);
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentShutdown>(OnSolutionShutdown);
|
||||
|
|
@ -95,6 +103,22 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnSolutionGetState(Entity<SolutionComponent> ent, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new SolutionComponentState(ent.Comp.Solution);
|
||||
}
|
||||
|
||||
private void OnSolutionHandleState(Entity<SolutionComponent> ent, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not SolutionComponentState cast)
|
||||
return;
|
||||
|
||||
ent.Comp.Solution = cast.Solution.Clone();
|
||||
|
||||
// Always raise the event on the client so that we can update UIs accordingly.
|
||||
var changedEv = new SolutionChangedEvent(ent);
|
||||
RaiseLocalEvent(ent, ref changedEv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to resolve a solution associated with an entity.
|
||||
|
|
@ -434,6 +458,15 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
|||
UpdateChemicals(soln);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether or not the given solution entity can react and dirties it.
|
||||
/// </summary>
|
||||
public void SetCanReact(Entity<SolutionComponent> soln, bool canReact)
|
||||
{
|
||||
soln.Comp.Solution.CanReact = canReact;
|
||||
UpdateChemicals(soln);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds reagent of an Id to the container.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ public sealed partial class ReagentEntityConditionSystem : EntityConditionSystem
|
|||
{
|
||||
protected override void Condition(Entity<SolutionComponent> entity, ref EntityConditionEvent<ReagentCondition> args)
|
||||
{
|
||||
var quant = entity.Comp.Solution.GetTotalPrototypeQuantity(args.Condition.Reagent);
|
||||
var soln = entity.Comp.Solution;
|
||||
var quant = soln.GetTotalPrototypeQuantity(args.Condition.Reagent);
|
||||
|
||||
args.Result = quant >= args.Condition.Min && quant <= args.Condition.Max;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Content.Shared.Weapons.Melee;
|
|||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Fluids;
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ public abstract class SharedAbsorbentSystem : EntitySystem
|
|||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
@ -59,6 +61,10 @@ public abstract class SharedAbsorbentSystem : EntitySystem
|
|||
|
||||
private void OnAbsorbentSolutionChange(Entity<AbsorbentComponent> ent, ref SolutionContainerChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
if (!SolutionContainer.TryGetSolution(ent.Owner, ent.Comp.SolutionName, out _, out var solution))
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -114,6 +114,10 @@ public abstract partial class SharedPuddleSystem : EntitySystem
|
|||
|
||||
private void OnSolutionUpdate(Entity<PuddleComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
if (args.SolutionId != entity.Comp.SolutionName)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ public sealed class FoodSequenceSystem : SharedFoodSequenceSystem
|
|||
return;
|
||||
|
||||
_solutionContainer.RemoveAllSolution(resultSoln.Value); //Remove all YML reagents
|
||||
resultSoln.Value.Comp.Solution.MaxVolume = startSoln.Value.Comp.Solution.MaxVolume;
|
||||
_solutionContainer.SetCapacity(resultSoln.Value, startSoln.Value.Comp.Solution.MaxVolume);
|
||||
_solutionContainer.TryAddSolution(resultSoln.Value, startSolution);
|
||||
|
||||
MergeFlavorProfiles(start, result);
|
||||
|
|
|
|||
|
|
@ -163,6 +163,10 @@ public sealed partial class IngestionSystem : EntitySystem
|
|||
|
||||
private void OnSolutionContainerChanged(Entity<EdibleComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
UpdateAppearance(entity);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -267,6 +267,10 @@ public sealed partial class PressurizedSolutionSystem : EntitySystem
|
|||
|
||||
private void OnSolutionUpdate(Entity<PressurizedSolutionComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
if (args.SolutionId != entity.Comp.Solution)
|
||||
return;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue