Add exception tolerance to SharedDoAfterSystem. (#43088)

This commit is contained in:
Moony 2026-03-10 20:56:26 +01:00 committed by Coryler
parent 79590e2a69
commit 35177173e2
1 changed files with 49 additions and 3 deletions

View File

@ -2,14 +2,16 @@ using Content.Shared.Gravity;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Physics;
using Robust.Shared.Utility;
using Robust.Shared.Exceptions;
using Robust.Shared.Network;
namespace Content.Shared.DoAfter;
public abstract partial class SharedDoAfterSystem : EntitySystem
{
[Dependency] private readonly IDynamicTypeFactory _factory = default!;
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
[Dependency] private readonly SharedGravitySystem _gravity = default!;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
@ -27,7 +29,51 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
var enumerator = EntityQueryEnumerator<ActiveDoAfterComponent, DoAfterComponent>();
while (enumerator.MoveNext(out var uid, out var active, out var comp))
{
Update(uid, active, comp, time, xformQuery, handsQuery);
try
{
Update(uid, active, comp, time, xformQuery, handsQuery);
}
// ReSharper disable once RedundantCatchClause
catch (Exception e)
{
#if EXCEPTION_TOLERANCE
// Doafter in question failed to complete..
// Doafters are kind of a critical game mechanic, so we specially handle failure.
_runtimeLog.LogException(e, $"{nameof(SharedDoAfterSystem)} on {ToPrettyString(uid)}");
if (_netManager.IsClient)
continue; // Move along, we can't cancel these ourselves and just need to not completely die.
// Cancel all the doafters for this entity to avoid repeats.
// We don't try to remove them ourselves to keep the logic reasonable.
foreach (var (key, doAfter) in comp.DoAfters)
{
try
{
InternalCancel(doAfter, comp);
}
catch (Exception e2)
{
_runtimeLog.LogException(e2, $"{nameof(SharedDoAfterSystem)} failed to cleanup {doAfter} @ {key} while handling a failure.");
// REMARK: As written, InternalCancel will always do the necessary side effect of
// configuring the cancellation time. We need this side effect, so dear reader
// if you ever make it so InternalCancel can throw an exception before that
// happens, update this to set cancel time itself in a finally block.
//
// If the doafter is one using async, this CAN result in that task leaking forever.
// So we check that here, too.
if (comp.AwaitedDoAfters.Remove(doAfter.Index, out var tcs))
{
tcs.TrySetCanceled();
}
}
}
#else
throw; // No tolerance, just rethrow.
#endif
}
}
}