Repository: Goob-Station/Goob-Station
Pull request #2744: delaggening
Author: login: Ilya246
Labels: name: size/M
description: Denotes a PR that changes 100-999 lines.
name: S: Untriaged
description: A professional will be with you shortly.
Created: 2025-05-12T07:05:30Z | Merged: 2025-05-12T07:26:35Z

<!--
SPDX-FileCopyrightText: 2021 Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
SPDX-FileCopyrightText: 2021 Swept <sweptwastaken@protonmail.com>
SPDX-FileCopyrightText: 2021 mirrorcult <lunarautomaton6@gmail.com>
SPDX-FileCopyrightText: 2022 AJCM-git <60196617+AJCM-git@users.noreply.github.com>
SPDX-FileCopyrightText: 2022 Kara <lunarautomaton6@gmail.com>
SPDX-FileCopyrightText: 2023 DrSmugleaf <DrSmugleaf@users.noreply.github.com>
SPDX-FileCopyrightText: 2023 Kevin Zheng <kevinz5000@gmail.com>
SPDX-FileCopyrightText: 2024 Vasilis <vasilis@pikachu.systems>
SPDX-FileCopyrightText: 2024 lzk <124214523+lzk228@users.noreply.github.com>
SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>

SPDX-License-Identifier: AGPL-3.0-or-later
-->

<!-- Guidelines: https://docs.spacestation14.io/en/getting-started/pr-guideline -->
<!-- NOTE: All code submitted to this repository is ALWAYS licensed under the AGPL-3.0-or-later license. 
The REUSE Specification headers or separate .license files indicate a secondary license (e.g., MPL or MIT) solely to facilitate 
integration for projects that do not use the AGPL license. This secondary license does not replace the fact that AGPL-3.0-or-later remains the primary and binding license. 
Uncomment and modify the following line if you wish to change the license from the default of AGPL.-->
<!--- LICENSE: AGPL -->
## About the PR
gus request

**Changelog**
<!-- Add a Changelog entry to make players aware of new features or changes that could affect gameplay.
Make sure to read the guidelines and take this Changelog template out of the comment block in order for it to show up.
Changelog must have a :cl: symbol, so the bot recognizes the changes and adds them to the game's changelog. -->
:cl:
- fix: Woundmed shouldn't passively lag out the server anymore.


Diff:
diff --git a/Content.Shared/Body/Components/BodyComponent.cs b/Content.Shared/Body/Components/BodyComponent.cs
index e8b1bb7ec3f3..07622e7524d0 100644
--- a/Content.Shared/Body/Components/BodyComponent.cs
+++ b/Content.Shared/Body/Components/BodyComponent.cs
@@ -7,6 +7,8 @@
 // SPDX-FileCopyrightText: 2023 metalgearsloth <comedian_vs_clown@hotmail.com>
 // SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
 // SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com>
+// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
+// SPDX-FileCopyrightText: 2025 Ilya246 <ilyukarno@gmail.com>
 // SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com>
 //
 // SPDX-License-Identifier: AGPL-3.0-or-later
@@ -17,6 +19,7 @@
 using Robust.Shared.Containers;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
+using System; // Goobstation
 
 namespace Content.Shared.Body.Components;
 
@@ -59,4 +62,11 @@ public sealed partial class BodyComponent : Component
     [DataField, AutoNetworkedField]
     public bool ThermalVisibility = true;
     // WD EDIT END
-}
\ No newline at end of file
+
+    // Goobstation
+    /// <summary>
+    /// When should  wounds on this be healed.
+    /// </summary>
+    [ViewVariables, AutoNetworkedField, Access(Other = AccessPermissions.ReadWrite)]
+    public TimeSpan HealAt;
+}
diff --git a/Content.Shared/_Shitmed/Surgery/Wounds/Components/WoundableComponent.cs b/Content.Shared/_Shitmed/Surgery/Wounds/Components/WoundableComponent.cs
index 8edc2d0aad09..3d9ae461b47d 100644
--- a/Content.Shared/_Shitmed/Surgery/Wounds/Components/WoundableComponent.cs
+++ b/Content.Shared/_Shitmed/Surgery/Wounds/Components/WoundableComponent.cs
@@ -1,4 +1,10 @@
-﻿using Content.Shared.Damage;
+// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
+// SPDX-FileCopyrightText: 2025 Ilya246 <ilyukarno@gmail.com>
+// SPDX-FileCopyrightText: 2025 gluesniffler <159397573+gluesniffler@users.noreply.github.com>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+using Content.Shared.Damage;
 using Content.Shared.Damage.Prototypes;
 using Content.Goobstation.Maths.FixedPoint;
 using Robust.Shared.Audio;
@@ -110,12 +116,6 @@ public sealed partial class WoundableComponent : Component
     [DataField]
     public WoundableSeverity WoundableSeverity;
 
-    /// <summary>
-    /// How much time in seconds had this woundable accumulated from the last healing tick.
-    /// </summary>
-    [ViewVariables]
-    public float HealingRateAccumulated;
-
     /// <summary>
     /// Container potentially holding wounds.
     /// </summary>
diff --git a/Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.Wounding.cs b/Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.Wounding.cs
index 3ffa1d61cd62..7a067c50bee8 100644
--- a/Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.Wounding.cs
+++ b/Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.Wounding.cs
@@ -1,4 +1,10 @@
-﻿using System.Diagnostics.CodeAnalysis;
+// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
+// SPDX-FileCopyrightText: 2025 Ilya246 <ilyukarno@gmail.com>
+// SPDX-FileCopyrightText: 2025 gluesniffler <159397573+gluesniffler@users.noreply.github.com>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using Content.Shared._Shitmed.CCVar;
 using Content.Shared._Shitmed.DoAfter;
@@ -58,6 +64,12 @@ private void OnWoundableInit(EntityUid uid, WoundableComponent comp, ComponentIn
         comp.RootWoundable = uid;
         comp.Wounds = _container.EnsureContainer<Container>(uid, WoundContainerId);
         comp.Bone = _container.EnsureContainer<Container>(uid, BoneContainerId);
+
+        if (TryComp<BodyComponent>(uid, out var body) && _timing.IsFirstTimePredicted)
+        {
+            body.HealAt = _timing.CurTime + TimeSpan.FromSeconds(1f / _medicalHealingTickrate);
+            _healQueue.Enqueue((uid, comp));
+        }
     }
 
     private void OnWoundableMapInit(EntityUid uid, WoundableComponent comp, MapInitEvent args)
diff --git a/Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs b/Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs
index ffb1ae059fa5..80164432802b 100644
--- a/Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs
+++ b/Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs
@@ -1,4 +1,11 @@
-﻿using Content.Shared._Shitmed.CCVar;
+// SPDX-FileCopyrightText: 2025 August Eymann <august.eymann@gmail.com>
+// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
+// SPDX-FileCopyrightText: 2025 Ilya246 <ilyukarno@gmail.com>
+// SPDX-FileCopyrightText: 2025 gluesniffler <159397573+gluesniffler@users.noreply.github.com>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+using Content.Shared._Shitmed.CCVar;
 using Content.Shared._Shitmed.Medical.Surgery.Traumas.Components;
 using Content.Shared._Shitmed.Medical.Surgery.Traumas.Systems;
 using Content.Shared._Shitmed.Medical.Surgery.Wounds.Components;
@@ -6,11 +13,9 @@
 using Content.Shared.Body.Part;
 using Content.Shared.Body.Systems;
 using Content.Shared.Damage;
-using Content.Shared.Damage.Prototypes;
 using Content.Goobstation.Maths.FixedPoint;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.Inventory;
-using Content.Shared.Mobs.Systems;
 using Content.Shared.Popups;
 using Content.Shared.Throwing;
 using Robust.Shared.Audio.Systems;
@@ -21,7 +26,6 @@
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using Robust.Shared.Timing;
-using Robust.Shared.Threading;
 using System.Linq;
 
 namespace Content.Shared._Shitmed.Medical.Surgery.Wounds.Systems;
@@ -36,23 +40,24 @@ public sealed partial class WoundSystem : EntitySystem
     [Dependency] private readonly INetManager _net = default!;
     [Dependency] private readonly IConfigurationManager _cfg = default!;
     [Dependency] private readonly IGameTiming _timing = default!;
-    [Dependency] private readonly IParallelManager _parallel = default!;
 
     [Dependency] private readonly SharedBodySystem _body = default!;
-    [Dependency] private readonly MobStateSystem _mobState = default!;
     [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
     [Dependency] private readonly SharedContainerSystem _container = default!;
     [Dependency] private readonly SharedTransformSystem _transform = default!;
     [Dependency] private readonly SharedPopupSystem _popup = default!;
     [Dependency] private readonly SharedHandsSystem _hands = default!;
     [Dependency] private readonly SharedAudioSystem _audio = default!;
+
     [Dependency] private readonly DamageableSystem _damageable = default!;
+
     // I'm the one.... who throws........
     [Dependency] private readonly ThrowingSystem _throwing = default!;
     [Dependency] private readonly InventorySystem _inventory = default!;
     [Dependency] private readonly TraumaSystem _trauma = default!;
 
     private float _medicalHealingTickrate = 2f;
+    private Queue<Entity<WoundableComponent?>> _healQueue = new(); // evil stateful data in system
 
     public override void Initialize()
     {
@@ -63,7 +68,6 @@ public override void Initialize()
         SubscribeLocalEvent<WoundableComponent, ComponentHandleState>(OnWoundableComponentHandleState);
         InitWounding();
         Subs.CVar(_cfg, SurgeryCVars.MedicalHealingTickrate, val => _medicalHealingTickrate = val, true);
-
     }
 
     private void OnWoundComponentGet(EntityUid uid, WoundComponent comp, ref ComponentGetState args)
@@ -71,7 +75,9 @@ private void OnWoundComponentGet(EntityUid uid, WoundComponent comp, ref Compone
         var state = new WoundComponentState
         {
             HoldingWoundable =
-                TryGetNetEntity(comp.HoldingWoundable, out var holdingWoundable) ? holdingWoundable.Value : NetEntity.Invalid,
+                TryGetNetEntity(comp.HoldingWoundable, out var holdingWoundable)
+                    ? holdingWoundable.Value
+                    : NetEntity.Invalid,
 
             WoundSeverityPoint = comp.WoundSeverityPoint,
             WoundableIntegrityMultiplier = comp.WoundableIntegrityMultiplier,
@@ -132,11 +138,14 @@ private void OnWoundComponentHandleState(EntityUid uid, WoundComponent component
                 }
             }
         }
+
         component.HoldingWoundable = holdingWoundable;
 
         if (component.WoundSeverityPoint != state.WoundSeverityPoint)
         {
-            var ev = new WoundSeverityPointChangedEvent(component, component.WoundSeverityPoint, state.WoundSeverityPoint);
+            var ev = new WoundSeverityPointChangedEvent(component,
+                component.WoundSeverityPoint,
+                state.WoundSeverityPoint);
             RaiseLocalEvent(uid, ref ev);
 
             // TODO: On body changed events aren't predicted, welp
@@ -176,13 +185,15 @@ private void OnWoundableComponentGet(EntityUid uid, WoundableComponent comp, ref
         var state = new WoundableComponentState
         {
             ParentWoundable = TryGetNetEntity(comp.ParentWoundable, out var parentWoundable) ? parentWoundable : null,
-            RootWoundable = TryGetNetEntity(comp.RootWoundable, out var rootWoundable) ? rootWoundable.Value : NetEntity.Invalid,
+            RootWoundable = TryGetNetEntity(comp.RootWoundable, out var rootWoundable)
+                ? rootWoundable.Value
+                : NetEntity.Invalid,
 
             ChildWoundables =
                 comp.ChildWoundables
                     .Select(woundable => TryGetNetEntity(woundable, out var ne)
-                    ? ne.Value
-                    : NetEntity.Invalid)
+                        ? ne.Value
+                        : NetEntity.Invalid)
                     .ToHashSet(),
             // Attached and Detached -Woundable events are handled on client with containers
 
@@ -198,16 +209,17 @@ private void OnWoundableComponentGet(EntityUid uid, WoundableComponent comp, ref
             SeverityMultipliers =
                 comp.SeverityMultipliers
                     .Select(multiplier
-                        => (TryGetNetEntity(multiplier.Key, out var ne) ? ne.Value : NetEntity.Invalid, multiplier.Value))
+                        => (TryGetNetEntity(multiplier.Key, out var ne) ? ne.Value : NetEntity.Invalid,
+                            multiplier.Value))
                     .ToDictionary(),
             HealingMultipliers =
                 comp.HealingMultipliers
                     .Select(multiplier
-                        => (TryGetNetEntity(multiplier.Key, out var ne) ? ne.Value : NetEntity.Invalid, multiplier.Value))
+                        => (TryGetNetEntity(multiplier.Key, out var ne) ? ne.Value : NetEntity.Invalid,
+                            multiplier.Value))
                     .ToDictionary(),
 
             WoundableSeverity = comp.WoundableSeverity,
-            HealingRateAccumulated = comp.HealingRateAccumulated,
         };
 
         args.State = state;
@@ -224,8 +236,8 @@ private void OnWoundableComponentHandleState(EntityUid uid, WoundableComponent c
 
         component.ChildWoundables = state.ChildWoundables
             .Select(x => TryGetEntity(x, out var y) ? y.Value : EntityUid.Invalid)
-                .Where(x => x.Valid)
-                .ToHashSet();
+            .Where(x => x.Valid)
+            .ToHashSet();
         // Attached and Detached -Woundable events are handled on client with containers
 
         component.AllowWounds = state.AllowWounds;
@@ -279,6 +291,7 @@ private void OnWoundableComponentHandleState(EntityUid uid, WoundableComponent c
                 RaiseLocalEvent(bodyPart.Body.Value, ref ev1);
             }
         }
+
         component.WoundableIntegrity = state.WoundableIntegrity;
 
         if (component.WoundableSeverity != state.WoundableSeverity)
@@ -286,65 +299,79 @@ private void OnWoundableComponentHandleState(EntityUid uid, WoundableComponent c
             var ev = new WoundableSeverityChangedEvent(component.WoundableSeverity, state.WoundableSeverity);
             RaiseLocalEvent(uid, ref ev);
         }
-        component.WoundableSeverity = state.WoundableSeverity;
 
-        component.HealingRateAccumulated = state.HealingRateAccumulated;
+        component.WoundableSeverity = state.WoundableSeverity;
     }
 
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
 
-        if (!_timing.IsFirstTimePredicted)
+        if (!_timing.IsFirstTimePredicted || _healQueue.Count == 0)
             return;
 
-        var timeToHeal = 1 / _medicalHealingTickrate;
-        var query = EntityQueryEnumerator<WoundableComponent, MetaDataComponent>();
-        while (query.MoveNext(out var ent, out var woundable, out var metaData))
+        var beginEnt = _healQueue.Peek();
+        do
         {
-            if (Paused(ent, metaData))
+            var ent = _healQueue.Peek();
+            BodyComponent? body = null;
+            // remove it from queue and go on if it's not real
+            if (TerminatingOrDeleted(ent) || !Resolve(ent, ref ent.Comp, ref body))
+            {
+                _healQueue.Dequeue();
+                if (ent == beginEnt && _healQueue.Count != 0)
+                    beginEnt = _healQueue.Peek();
                 continue;
+            }
 
-            woundable.HealingRateAccumulated += frameTime;
-            if (woundable.HealingRateAccumulated < timeToHeal)
-                continue;
+            // queue is supposed to be sorted time-wise
+            if (body.HealAt > _timing.CurTime)
+                break;
 
-            if (woundable.Wounds == null || woundable.Wounds.Count == 0)
+            // it's real and it's time, move it to the end of the queue
+            body.HealAt += TimeSpan.FromSeconds(1f / _medicalHealingTickrate);
+            _healQueue.Dequeue();
+            _healQueue.Enqueue(ent);
+
+            if (Paused(ent) || !_body.TryGetRootPart(ent, out var rootPart, body: body))
                 continue;
 
-            woundable.HealingRateAccumulated -= timeToHeal;
+            foreach (var woundable in GetAllWoundableChildren(rootPart.Value))
+                ProcessHealing(woundable, frameTime);
 
-            var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(ent, woundable)
-                .Where(wound => wound.Comp2.BleedingAmount > 0)
-                .ToArray();
+        } while (_healQueue.Count != 0 && _healQueue.Peek() != beginEnt);
+    }
 
-            var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
-                    (current, wound) => current + wound.Comp2.BleedingAmount);
+    private void ProcessHealing(Entity<WoundableComponent> woundable, float frameTime)
+    {
+        var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(woundable)
+            .Where(wound => wound.Comp2.BleedingAmount > 0)
+            .ToArray();
 
-            if (bleedingAmount > woundable.BleedsThreshold)
-                continue;
+        var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
+            (current, wound) => current + wound.Comp2.BleedingAmount);
 
-            if (bleedWounds.Length > 0)
-            {
-                var bleedTreatment = woundable.BleedingTreatmentAbility / bleedWounds.Length;
-                var result = TryHealBleedingWounds(ent, (float) -bleedTreatment, out var modifiedBleed, woundable);
-            }
+        if (bleedingAmount > woundable.Comp.BleedsThreshold)
+            return;
+
+        if (bleedWounds.Length > 0)
+        {
+            var bleedTreatment = woundable.Comp.BleedingTreatmentAbility / bleedWounds.Length;
+            TryHealBleedingWounds(woundable, (float) -bleedTreatment, out _, woundable);
+        }
 
-            var woundsToHeal =
-                GetWoundableWounds(ent, woundable).Where(wound => CanHealWound(wound, wound)).ToList();
+        var woundsToHeal = GetWoundableWounds(woundable).Where(wound => CanHealWound(wound, wound)).ToList();
 
-            if (woundsToHeal.Count == 0)
-                continue;
+        if (woundsToHeal.Count == 0)
+            return;
 
-            var healAmount = -woundable.HealAbility / woundsToHeal.Count;
+        var healAmount = -woundable.Comp.HealAbility / woundsToHeal.Count;
 
-            Entity<WoundableComponent> owner = (ent, woundable);
-            foreach (var x in woundsToHeal)
-            {
-                ApplyWoundSeverity(x,
-                    ApplyHealingRateMultipliers(x, owner, healAmount, owner, x),
-                    x);
-            }
+        foreach (var x in woundsToHeal)
+        {
+            ApplyWoundSeverity(x,
+                ApplyHealingRateMultipliers(x, woundable, healAmount),
+                x);
         }
     }
 }


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>Content.Shared/_Shitmed/Surgery/Wounds/Components/WoundableComponent.cs (1)</summary><blockquote>

`170-173`: _⚠️ Potential issue_

**⚠️ Specter of desynchronisation: `HealingRateAccumulated` lingers only in net-state**  

By the beard of Knuth, the field `HealingRateAccumulated` hath been exorcised from the runtime component above, yet its ghost endureth inside `WoundableComponentState` (lines 172-173).  
When a field exists in state but not in the authoritative component, `ComponentState` serialisation shall still attempt to read / write it, yielding mismatched payloads, wasted bandwidth, or, in the darkest case, runtime crashes when the client expects the datum to materialise.

I counsel thee to purge the vestigial field (and any related logic) or to re-add it deliberately with clarifying comments.

```diff
-    public float HealingRateAccumulated;
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs (2)</summary><blockquote>

`60-61`: **Stateful queue declared with nullable component – unnecessary complexity**  

`Queue<Entity<WoundableComponent?>>` allows enqueuing entities devoid of the coveted component, yet enqueue sites always furnish a non-null `comp`. A stricter type (`Entity<WoundableComponent>`) would banish errant entries at compile-time and spare run-time null checks.

```diff
-    private Queue<Entity<WoundableComponent?>> _healQueue = new();
+    private readonly Queue<Entity<WoundableComponent>> _healQueue = new();
```

---

`345-376`: **`ProcessHealing` divineth by zero & repeats work**  

1. Division by the count of healable wounds (`woundsToHeal.Count`) or bleed wounds (`bleedWounds.Length`) is righteous, yet the method re-computes both lists separately each tick, doubling enumeration and allocations. Cache them once.

2. If `woundsToHeal.Count == 0` thou returnest *after* the bleed treatment branch; thus bleeding may be staunched even when wounds surpass the `BleedsThreshold`. Confirm intent.

Example tightening:

```diff
-        var bleedWounds = ...
-        var bleedingAmount = ...
+        var bleedWounds = ...ToArray();
+        var bleedingAmount = bleedWounds.Sum(b => b.Comp2.BleedingAmount);

         if (bleedingAmount > woundable.Comp.BleedsThreshold)
             return;

-        if (bleedWounds.Length > 0)
+        if (bleedWounds.Length != 0)
             ...

-        var woundsToHeal = GetWoundableWounds(woundable)
-            .Where(wound => CanHealWound(wound, wound)).ToList();
+        var woundsToHeal = GetWoundableWounds(woundable)
+                             .Where(w => CanHealWound(w, w))
+                             .ToArray();
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between bae950a4c8c38d3e1538ea6898a80ac4780f6cd4 and 99f52801039b3a6109b00d60382d24ea360427a9.

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `Content.Shared/Body/Components/BodyComponent.cs` (3 hunks)
* `Content.Shared/_Shitmed/Surgery/Wounds/Components/WoundableComponent.cs` (1 hunks)
* `Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.Wounding.cs` (2 hunks)
* `Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs` (8 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs (2)</summary><blockquote>

<details>
<summary>Content.Shared/Damage/Systems/DamageableSystem.cs (1)</summary>

* `DamageableSystem` (97-801)

</details>
<details>
<summary>Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.Wounding.cs (4)</summary>

* `FixedPoint2` (592-606)
* `FixedPoint2` (1666-1689)
* `FixedPoint2` (1699-1722)
* `ApplyWoundSeverity` (535-590)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: Test Packaging
* GitHub Check: build (ubuntu-latest)
* GitHub Check: YAML Linter
* GitHub Check: YAML map schema validator
* GitHub Check: build (ubuntu-latest)

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>Content.Shared/Body/Components/BodyComponent.cs (1)</summary>

`66-72`: **`HealAt` lacks a defined origin – may schedule immediate healing at time 0**  

`TimeSpan` defaulteth to `0:00:00`; for bodies spawned *before* any `WoundableComponent` tick, the queue shall deem them overdue and process forthwith, even in the absence of wounds.  
Declare a sentinel (e.g., `TimeSpan.MaxValue`) or initialise in `Initialize()` so that only the first legitimate scheduling sets the hour.

```diff
-    public TimeSpan HealAt;
+    [ViewVariables, AutoNetworkedField, Access(Other = AccessPermissions.ReadWrite)]
+    public TimeSpan HealAt = TimeSpan.MaxValue; // healed when explicitly scheduled
```

</details>
<details>
<summary>Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs (1)</summary>

`310-343`: _⚠️ Potential issue_

**Healing loop can empty the queue then `Peek` an empty abyss**  

Inside the do-while thou `Dequeue()` and immediately call `_healQueue.Peek()` without re-checking `Count`. Should that dequeue leave the queue barren, an `InvalidOperationException` shall erupt.

Guard thy peek, sorcerer:

```diff
-                if (ent == beginEnt && _healQueue.Count != 0)
-                    beginEnt = _healQueue.Peek();
+                if (ent == beginEnt && _healQueue.Count != 0)
+                    beginEnt = _healQueue.Peek();
+                if (_healQueue.Count == 0)
+                    break;
```

…and ensure the while-condition itself surviveth an empty queue.





> Likely an incorrect or invalid review comment.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T07:13:31Z
user: login: SX-7
body: Can be improved, but I'm leaving it to gus
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T07:15:14Z
user: login: Ilya246
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T07:16:40Z
user: login: SX-7
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T07:17:36Z
user: login: SX-7
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T07:17:59Z
user: login: gusxyz
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T07:26:33Z

Review comments:
diff_hunk: @@ -58,6 +64,12 @@ private void OnWoundableInit(EntityUid uid, WoundableComponent comp, ComponentIn
         comp.RootWoundable = uid;
         comp.Wounds = _container.EnsureContainer<Container>(uid, WoundContainerId);
         comp.Bone = _container.EnsureContainer<Container>(uid, BoneContainerId);
+
+        if (TryComp<BodyComponent>(uid, out var body) && _timing.IsFirstTimePredicted)
+        {
+            body.HealAt = _timing.CurTime + TimeSpan.FromSeconds(1f / _medicalHealingTickrate);
+            _healQueue.Enqueue((uid, comp));
+        }
path: Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.Wounding.cs
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Duplicate-queue peril within `OnWoundableInit`**  

Thou enqueue the parent body (`uid`) every time a `WoundableComponent` awakens. Should the component re-initialise (e.g. map-expose, split-client prediction, etc.) this clause re-fires, spawning twin entries in `_healQueue`.  
Lest the healing loop labour twice upon the same body each tick, guard the call:

```diff
-            _healQueue.Enqueue((uid, comp));
+            if (!_healQueue.Any(e => e.Owner == uid))
+                _healQueue.Enqueue((uid, comp));
```

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.Wounding.cs around
lines 68 to 72, the code enqueues the same body (uid) into _healQueue every time
a WoundableComponent initializes, causing duplicate entries and redundant
healing work. To fix this, add a check before enqueueing to ensure the uid is
not already present in _healQueue, preventing duplicate queue entries and
avoiding repeated healing processing for the same body within a tick.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-12T07:13:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -279,72 +291,87 @@ private void OnWoundableComponentHandleState(EntityUid uid, WoundableComponent c
                 RaiseLocalEvent(bodyPart.Body.Value, ref ev1);
             }
         }
+
         component.WoundableIntegrity = state.WoundableIntegrity;
 
         if (component.WoundableSeverity != state.WoundableSeverity)
         {
             var ev = new WoundableSeverityChangedEvent(component.WoundableSeverity, state.WoundableSeverity);
             RaiseLocalEvent(uid, ref ev);
         }
-        component.WoundableSeverity = state.WoundableSeverity;
 
-        component.HealingRateAccumulated = state.HealingRateAccumulated;
+        component.WoundableSeverity = state.WoundableSeverity;
     }
 
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
 
-        if (!_timing.IsFirstTimePredicted)
+        if (!_timing.IsFirstTimePredicted || _healQueue.Count == 0)
             return;
 
-        var timeToHeal = 1 / _medicalHealingTickrate;
-        var query = EntityQueryEnumerator<WoundableComponent, MetaDataComponent>();
-        while (query.MoveNext(out var ent, out var woundable, out var metaData))
+        var beginEnt = _healQueue.Peek();
+        do
         {
-            if (Paused(ent, metaData))
+            var ent = _healQueue.Peek();
+            BodyComponent? body = null;
+            // remove it from queue and go on if it's not real
+            if (TerminatingOrDeleted(ent) || !Resolve(ent, ref ent.Comp, ref body))
+            {
+                _healQueue.Dequeue();
+                if (ent == beginEnt && _healQueue.Count != 0)
+                    beginEnt = _healQueue.Peek();
                 continue;
+            }
 
-            woundable.HealingRateAccumulated += frameTime;
-            if (woundable.HealingRateAccumulated < timeToHeal)
-                continue;
+            // queue is supposed to be sorted time-wise
+            if (body.HealAt > _timing.CurTime)
+                break;
 
-            if (woundable.Wounds == null || woundable.Wounds.Count == 0)
+            // it's real and it's time, move it to the end of the queue
+            body.HealAt += TimeSpan.FromSeconds(1f / _medicalHealingTickrate);
+            _healQueue.Dequeue();
+            _healQueue.Enqueue(ent);
+
+            if (Paused(ent) || !_body.TryGetRootPart(ent, out var rootPart, body: body))
                 continue;
 
-            woundable.HealingRateAccumulated -= timeToHeal;
+            foreach (var woundable in GetAllWoundableChildren(rootPart.Value))
+                ProcessHealing(woundable, frameTime);
 
-            var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(ent, woundable)
-                .Where(wound => wound.Comp2.BleedingAmount > 0)
-                .ToArray();
+        } while (_healQueue.Count != 0 && _healQueue.Peek() != beginEnt);
+    }
 
-            var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
-                    (current, wound) => current + wound.Comp2.BleedingAmount);
+    private void ProcessHealing(Entity<WoundableComponent> woundable, float frameTime)
+    {
+        var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(woundable)
+            .Where(wound => wound.Comp2.BleedingAmount > 0)
+            .ToArray();
 
-            if (bleedingAmount > woundable.BleedsThreshold)
-                continue;
+        var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
+            (current, wound) => current + wound.Comp2.BleedingAmount);
path: Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs
user: login: SX-7
body: is this linq in Update type of function? consider using something less laggy
same in other examples here
created_at: 2025-05-12T07:14:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -279,72 +291,87 @@ private void OnWoundableComponentHandleState(EntityUid uid, WoundableComponent c
                 RaiseLocalEvent(bodyPart.Body.Value, ref ev1);
             }
         }
+
         component.WoundableIntegrity = state.WoundableIntegrity;
 
         if (component.WoundableSeverity != state.WoundableSeverity)
         {
             var ev = new WoundableSeverityChangedEvent(component.WoundableSeverity, state.WoundableSeverity);
             RaiseLocalEvent(uid, ref ev);
         }
-        component.WoundableSeverity = state.WoundableSeverity;
 
-        component.HealingRateAccumulated = state.HealingRateAccumulated;
+        component.WoundableSeverity = state.WoundableSeverity;
     }
 
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
 
-        if (!_timing.IsFirstTimePredicted)
+        if (!_timing.IsFirstTimePredicted || _healQueue.Count == 0)
             return;
 
-        var timeToHeal = 1 / _medicalHealingTickrate;
-        var query = EntityQueryEnumerator<WoundableComponent, MetaDataComponent>();
-        while (query.MoveNext(out var ent, out var woundable, out var metaData))
+        var beginEnt = _healQueue.Peek();
+        do
         {
-            if (Paused(ent, metaData))
+            var ent = _healQueue.Peek();
+            BodyComponent? body = null;
+            // remove it from queue and go on if it's not real
+            if (TerminatingOrDeleted(ent) || !Resolve(ent, ref ent.Comp, ref body))
+            {
+                _healQueue.Dequeue();
+                if (ent == beginEnt && _healQueue.Count != 0)
+                    beginEnt = _healQueue.Peek();
                 continue;
+            }
 
-            woundable.HealingRateAccumulated += frameTime;
-            if (woundable.HealingRateAccumulated < timeToHeal)
-                continue;
+            // queue is supposed to be sorted time-wise
+            if (body.HealAt > _timing.CurTime)
+                break;
 
-            if (woundable.Wounds == null || woundable.Wounds.Count == 0)
+            // it's real and it's time, move it to the end of the queue
+            body.HealAt += TimeSpan.FromSeconds(1f / _medicalHealingTickrate);
+            _healQueue.Dequeue();
+            _healQueue.Enqueue(ent);
+
+            if (Paused(ent) || !_body.TryGetRootPart(ent, out var rootPart, body: body))
                 continue;
 
-            woundable.HealingRateAccumulated -= timeToHeal;
+            foreach (var woundable in GetAllWoundableChildren(rootPart.Value))
+                ProcessHealing(woundable, frameTime);
 
-            var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(ent, woundable)
-                .Where(wound => wound.Comp2.BleedingAmount > 0)
-                .ToArray();
+        } while (_healQueue.Count != 0 && _healQueue.Peek() != beginEnt);
+    }
 
-            var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
-                    (current, wound) => current + wound.Comp2.BleedingAmount);
+    private void ProcessHealing(Entity<WoundableComponent> woundable, float frameTime)
+    {
+        var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(woundable)
+            .Where(wound => wound.Comp2.BleedingAmount > 0)
+            .ToArray();
 
-            if (bleedingAmount > woundable.BleedsThreshold)
-                continue;
+        var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
+            (current, wound) => current + wound.Comp2.BleedingAmount);
path: Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs
user: login: Ilya246
body: gus wrote this
created_at: 2025-05-12T07:16:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -279,72 +291,87 @@ private void OnWoundableComponentHandleState(EntityUid uid, WoundableComponent c
                 RaiseLocalEvent(bodyPart.Body.Value, ref ev1);
             }
         }
+
         component.WoundableIntegrity = state.WoundableIntegrity;
 
         if (component.WoundableSeverity != state.WoundableSeverity)
         {
             var ev = new WoundableSeverityChangedEvent(component.WoundableSeverity, state.WoundableSeverity);
             RaiseLocalEvent(uid, ref ev);
         }
-        component.WoundableSeverity = state.WoundableSeverity;
 
-        component.HealingRateAccumulated = state.HealingRateAccumulated;
+        component.WoundableSeverity = state.WoundableSeverity;
     }
 
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
 
-        if (!_timing.IsFirstTimePredicted)
+        if (!_timing.IsFirstTimePredicted || _healQueue.Count == 0)
             return;
 
-        var timeToHeal = 1 / _medicalHealingTickrate;
-        var query = EntityQueryEnumerator<WoundableComponent, MetaDataComponent>();
-        while (query.MoveNext(out var ent, out var woundable, out var metaData))
+        var beginEnt = _healQueue.Peek();
+        do
         {
-            if (Paused(ent, metaData))
+            var ent = _healQueue.Peek();
+            BodyComponent? body = null;
+            // remove it from queue and go on if it's not real
+            if (TerminatingOrDeleted(ent) || !Resolve(ent, ref ent.Comp, ref body))
+            {
+                _healQueue.Dequeue();
+                if (ent == beginEnt && _healQueue.Count != 0)
+                    beginEnt = _healQueue.Peek();
                 continue;
+            }
 
-            woundable.HealingRateAccumulated += frameTime;
-            if (woundable.HealingRateAccumulated < timeToHeal)
-                continue;
+            // queue is supposed to be sorted time-wise
+            if (body.HealAt > _timing.CurTime)
+                break;
 
-            if (woundable.Wounds == null || woundable.Wounds.Count == 0)
+            // it's real and it's time, move it to the end of the queue
+            body.HealAt += TimeSpan.FromSeconds(1f / _medicalHealingTickrate);
+            _healQueue.Dequeue();
+            _healQueue.Enqueue(ent);
+
+            if (Paused(ent) || !_body.TryGetRootPart(ent, out var rootPart, body: body))
                 continue;
 
-            woundable.HealingRateAccumulated -= timeToHeal;
+            foreach (var woundable in GetAllWoundableChildren(rootPart.Value))
+                ProcessHealing(woundable, frameTime);
 
-            var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(ent, woundable)
-                .Where(wound => wound.Comp2.BleedingAmount > 0)
-                .ToArray();
+        } while (_healQueue.Count != 0 && _healQueue.Peek() != beginEnt);
+    }
 
-            var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
-                    (current, wound) => current + wound.Comp2.BleedingAmount);
+    private void ProcessHealing(Entity<WoundableComponent> woundable, float frameTime)
+    {
+        var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(woundable)
+            .Where(wound => wound.Comp2.BleedingAmount > 0)
+            .ToArray();
 
-            if (bleedingAmount > woundable.BleedsThreshold)
-                continue;
+        var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
+            (current, wound) => current + wound.Comp2.BleedingAmount);
path: Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs
user: login: SX-7
body: I don't think I touched this code in my life
created_at: 2025-05-12T07:17:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -279,72 +291,87 @@ private void OnWoundableComponentHandleState(EntityUid uid, WoundableComponent c
                 RaiseLocalEvent(bodyPart.Body.Value, ref ev1);
             }
         }
+
         component.WoundableIntegrity = state.WoundableIntegrity;
 
         if (component.WoundableSeverity != state.WoundableSeverity)
         {
             var ev = new WoundableSeverityChangedEvent(component.WoundableSeverity, state.WoundableSeverity);
             RaiseLocalEvent(uid, ref ev);
         }
-        component.WoundableSeverity = state.WoundableSeverity;
 
-        component.HealingRateAccumulated = state.HealingRateAccumulated;
+        component.WoundableSeverity = state.WoundableSeverity;
     }
 
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
 
-        if (!_timing.IsFirstTimePredicted)
+        if (!_timing.IsFirstTimePredicted || _healQueue.Count == 0)
             return;
 
-        var timeToHeal = 1 / _medicalHealingTickrate;
-        var query = EntityQueryEnumerator<WoundableComponent, MetaDataComponent>();
-        while (query.MoveNext(out var ent, out var woundable, out var metaData))
+        var beginEnt = _healQueue.Peek();
+        do
         {
-            if (Paused(ent, metaData))
+            var ent = _healQueue.Peek();
+            BodyComponent? body = null;
+            // remove it from queue and go on if it's not real
+            if (TerminatingOrDeleted(ent) || !Resolve(ent, ref ent.Comp, ref body))
+            {
+                _healQueue.Dequeue();
+                if (ent == beginEnt && _healQueue.Count != 0)
+                    beginEnt = _healQueue.Peek();
                 continue;
+            }
 
-            woundable.HealingRateAccumulated += frameTime;
-            if (woundable.HealingRateAccumulated < timeToHeal)
-                continue;
+            // queue is supposed to be sorted time-wise
+            if (body.HealAt > _timing.CurTime)
+                break;
 
-            if (woundable.Wounds == null || woundable.Wounds.Count == 0)
+            // it's real and it's time, move it to the end of the queue
+            body.HealAt += TimeSpan.FromSeconds(1f / _medicalHealingTickrate);
+            _healQueue.Dequeue();
+            _healQueue.Enqueue(ent);
+
+            if (Paused(ent) || !_body.TryGetRootPart(ent, out var rootPart, body: body))
                 continue;
 
-            woundable.HealingRateAccumulated -= timeToHeal;
+            foreach (var woundable in GetAllWoundableChildren(rootPart.Value))
+                ProcessHealing(woundable, frameTime);
 
-            var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(ent, woundable)
-                .Where(wound => wound.Comp2.BleedingAmount > 0)
-                .ToArray();
+        } while (_healQueue.Count != 0 && _healQueue.Peek() != beginEnt);
+    }
 
-            var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
-                    (current, wound) => current + wound.Comp2.BleedingAmount);
+    private void ProcessHealing(Entity<WoundableComponent> woundable, float frameTime)
+    {
+        var bleedWounds = GetWoundableWoundsWithComp<BleedInflicterComponent>(woundable)
+            .Where(wound => wound.Comp2.BleedingAmount > 0)
+            .ToArray();
 
-            if (bleedingAmount > woundable.BleedsThreshold)
-                continue;
+        var bleedingAmount = bleedWounds.Aggregate(FixedPoint2.Zero,
+            (current, wound) => current + wound.Comp2.BleedingAmount);
path: Content.Shared/_Shitmed/Surgery/Wounds/Systems/WoundSystem.cs
user: login: SX-7
body: And even if, it should not be here
created_at: 2025-05-12T07:17:59Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-12T07:07:56Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

HEARKEN, MORTAL! A great refactoring hath transpired within the realm of wound healing. The sacred BodyComponent is now imbued with a public `HealAt` field, marking the destined hour for wounds to mend. The WoundableComponent hath shed its `HealingRateAccumulated` field, casting aside the old ways. In the WoundSystem, a mighty queue now governs the order of healing, processing entities based on their appointed `HealAt` time. The healing ritual is now orchestrated by a new `ProcessHealing` incantation, distributing healing and tending to bleeding wounds in due course. The Update method bows to this new order, forsaking the ancient global accumulation.

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant GameLoop
    participant WoundSystem
    participant Entity
    participant BodyComponent
    participant WoundableComponent

    GameLoop->>WoundSystem: Update(frameTime)
    loop For each entity in _healQueue
        WoundSystem->>Entity: Check if terminating/deleted/unresolved
        alt Entity invalid
            WoundSystem->>WoundSystem: Remove from _healQueue
        else Entity valid
            WoundSystem->>BodyComponent: Check HealAt <= now
            alt HealAt not due
                WoundSystem->>WoundSystem: Break loop
            else HealAt due
                WoundSystem->>WoundSystem: ProcessHealing(entity, frameTime)
                WoundSystem->>BodyComponent: Update HealAt
                WoundSystem->>WoundSystem: Re-enqueue entity
            end
        end
    end
```

```mermaid
sequenceDiagram
    participant WoundableComponent
    participant BodyComponent
    participant WoundSystem

    WoundableComponent->>WoundSystem: OnWoundableInit
    WoundSystem->>BodyComponent: Set HealAt = now + healing interval
    WoundSystem->>WoundSystem: Enqueue entity in _healQueue
```

## Suggested labels

`Large Bug`


<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Goob-Station/Goob-Station&utm_content=2744):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: gusxyz
created_at: 2025-05-12T07:20:49Z
body: I dodnt write those linq functions dumbasssses
author_association: CONTRIBUTOR
user: login: Ilya246
created_at: 2025-05-12T07:23:40Z
body: > I dodnt write those linq functions dumbasssses

real
me when neither i or sx actually bother to read the commit
author_association: CONTRIBUTOR