< Summary

Information
Class: Gravitas.RetainedPartitionLifecycle
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/Partitions/RetainedPartitionLifecycle.cs
Line coverage
100%
Covered lines: 87
Uncovered lines: 0
Coverable lines: 87
Total lines: 240
Line coverage: 100%
Branch coverage
100%
Covered branches: 68
Total branches: 68
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
DetachAll(...)100%1212100%
Track(...)100%22100%
Untrack(...)100%1010100%
RetireExpired(...)100%1212100%
TryRetireEmptyForReuse(...)100%1414100%
FindIndex(...)100%44100%
ShouldRetire(...)100%88100%
Retire(...)100%66100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Partitions/RetainedPartitionLifecycle.cs

#LineLine coverage
 1//=======================================================================
 2// RetainedPartitionLifecycle.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2026-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using GridForge.Grids;
 9using GridForge.Spatial;
 10using SwiftCollections;
 11using System;
 12
 13namespace Gravitas;
 14
 15internal interface IRetainedPhysicsPartition<in TOwner> : IVoxelPartition
 16{
 17    int RetainedIndex { get; }
 18
 19    bool IsEmpty { get; }
 20
 21    bool IsAllocated { get; }
 22
 23    int EmptySinceFrame { get; }
 24
 25    bool IsOwnedBy(TOwner owner);
 26
 27    void SetRetainedIndex(int index);
 28
 29    void ClearRetainedIndex();
 30}
 31
 32internal static class RetainedPartitionLifecycle
 33{
 34    internal static void DetachAll<TPartition, TOwner>(
 35        SwiftList<TPartition> retainedPartitions,
 36        GridWorld world,
 37        TOwner owner,
 38        Action<TPartition> releasePartition,
 39        string partitionName,
 40        string detachError)
 41        where TPartition : class, IRetainedPhysicsPartition<TOwner>
 42    {
 43        // Reset is a context boundary; retained GridForge payloads are a runtime cache, not replay state.
 73544        while (retainedPartitions.Count > 0)
 45        {
 64046            TPartition partition = retainedPartitions[retainedPartitions.Count - 1];
 64047            if (!partition.IsOwnedBy(owner))
 48            {
 149                int disabledCursor = -1;
 150                Untrack(retainedPartitions, owner, partition, ref disabledCursor);
 151                continue;
 52            }
 53
 63954            if (world.TryGetVoxel(partition.WorldIndex, out Voxel? voxel)
 63955                && voxel!.TryGetPartition(out TPartition? attachedPartition)
 63956                && ReferenceEquals(attachedPartition, partition))
 57            {
 63858                bool removed = voxel.TryRemovePartition<TPartition>();
 63859                SwiftThrowHelper.ThrowIfTrue(!removed, partitionName, detachError);
 60
 63861                if (partition.IsOwnedBy(owner))
 162                    releasePartition(partition);
 63
 164                continue;
 65            }
 66
 167            releasePartition(partition);
 68        }
 9569    }
 70
 71    internal static void Track<TPartition, TOwner>(
 72        SwiftList<TPartition> retainedPartitions,
 73        TOwner owner,
 74        TPartition partition,
 75        string partitionName)
 76        where TPartition : class, IRetainedPhysicsPartition<TOwner>
 77    {
 49357678        SwiftThrowHelper.ThrowIfNull(partition, nameof(partition));
 49357679        SwiftThrowHelper.ThrowIfArgument(
 49357680            partition.RetainedIndex >= 0,
 49357681            nameof(partition),
 49357682            $"{partitionName} is already tracked as retained.");
 83
 49357584        partition.SetRetainedIndex(retainedPartitions.Count);
 49357585        retainedPartitions.Add(partition);
 49357586    }
 87
 88    internal static void Untrack<TPartition, TOwner>(
 89        SwiftList<TPartition> retainedPartitions,
 90        TOwner owner,
 91        TPartition partition,
 92        ref int retirementCursor)
 93        where TPartition : class, IRetainedPhysicsPartition<TOwner>
 94    {
 49358695        int index = FindIndex(retainedPartitions, owner, partition);
 49358696        if (index < 0)
 97        {
 2298            partition.ClearRetainedIndex();
 2299            return;
 100        }
 101
 493564102        int lastIndex = retainedPartitions.Count - 1;
 493564103        if (index != lastIndex)
 104        {
 309998105            TPartition movedPartition = retainedPartitions[lastIndex];
 309998106            retainedPartitions[index] = movedPartition;
 309998107            movedPartition.SetRetainedIndex(index);
 108        }
 109
 493564110        retainedPartitions.RemoveAt(lastIndex);
 493564111        partition.ClearRetainedIndex();
 112
 493564113        if (retirementCursor < 0)
 2114            return;
 115
 493562116        if (retirementCursor > index)
 29215117            retirementCursor--;
 493562118        if (retirementCursor >= retainedPartitions.Count)
 8176119            retirementCursor = 0;
 493562120    }
 121
 122    internal static void RetireExpired<TPartition, TOwner>(
 123        SwiftList<TPartition> retainedPartitions,
 124        GridWorld world,
 125        TOwner owner,
 126        int budget,
 127        int currentFrame,
 128        int timeToKillFrames,
 129        Action<TPartition> releasePartition,
 130        ref int retirementCursor)
 131        where TPartition : class, IRetainedPhysicsPartition<TOwner>
 132    {
 3533133        if (budget <= 0 || retainedPartitions.Count == 0)
 38134            return;
 135
 3495136        int inspected = 0;
 230326137        while (inspected < budget && retainedPartitions.Count > 0)
 138        {
 226831139            if (retirementCursor >= retainedPartitions.Count)
 8805140                retirementCursor = 0;
 141
 226831142            TPartition partition = retainedPartitions[retirementCursor];
 226831143            inspected++;
 144
 226831145            if (!ShouldRetire(partition, owner, currentFrame, timeToKillFrames))
 146            {
 226629147                retirementCursor++;
 226629148                continue;
 149            }
 150
 202151            Retire(world, owner, partition, releasePartition);
 152        }
 3495153    }
 154
 155    internal static bool TryRetireEmptyForReuse<TPartition, TOwner>(
 156        SwiftList<TPartition> retainedPartitions,
 157        SwiftStack<TPartition> inactivePartitionPool,
 158        GridWorld world,
 159        TOwner owner,
 160        Action<TPartition> releasePartition,
 161        ref int retirementCursor)
 162        where TPartition : class, IRetainedPhysicsPartition<TOwner>
 163    {
 493476164        int inspected = 0;
 2147483647165        while (inspected < retainedPartitions.Count && retainedPartitions.Count > 0)
 166        {
 2147483647167            if (retirementCursor >= retainedPartitions.Count)
 434942168                retirementCursor = 0;
 169
 2147483647170            TPartition partition = retainedPartitions[retirementCursor];
 2147483647171            inspected++;
 172
 2147483647173            if (!partition.IsOwnedBy(owner) || !partition.IsEmpty || partition.IsAllocated)
 174            {
 2147483647175                retirementCursor++;
 2147483647176                continue;
 177            }
 178
 45563179            int poolCount = inactivePartitionPool.Count;
 45563180            Retire(world, owner, partition, releasePartition);
 45563181            if (inactivePartitionPool.Count > poolCount)
 45562182                return true;
 183
 1184            retirementCursor++;
 185        }
 186
 447914187        return false;
 188    }
 189
 190    private static int FindIndex<TPartition, TOwner>(
 191        SwiftList<TPartition> retainedPartitions,
 192        TOwner owner,
 193        TPartition partition)
 194        where TPartition : class, IRetainedPhysicsPartition<TOwner>
 195    {
 196        _ = owner;
 493586197        int index = partition.RetainedIndex;
 493586198        return (uint)index < (uint)retainedPartitions.Count && ReferenceEquals(retainedPartitions[index], partition)
 493586199            ? index
 493586200            : -1;
 201    }
 202
 203    private static bool ShouldRetire<TPartition, TOwner>(
 204        TPartition partition,
 205        TOwner owner,
 206        int currentFrame,
 207        int timeToKillFrames)
 208        where TPartition : class, IRetainedPhysicsPartition<TOwner>
 209    {
 226831210        if (!partition.IsOwnedBy(owner) || !partition.IsEmpty || partition.IsAllocated || partition.EmptySinceFrame < 0)
 211327211            return false;
 212
 15504213        int idleFrames = currentFrame - partition.EmptySinceFrame;
 15504214        return idleFrames >= timeToKillFrames;
 215    }
 216
 217    private static void Retire<TPartition, TOwner>(
 218        GridWorld world,
 219        TOwner owner,
 220        TPartition partition,
 221        Action<TPartition> releasePartition)
 222        where TPartition : class, IRetainedPhysicsPartition<TOwner>
 223    {
 224        _ = owner;
 45765225        if (!world.TryGetVoxel(partition.WorldIndex, out Voxel? voxel))
 226        {
 3227            releasePartition(partition);
 3228            return;
 229        }
 230
 45762231        if (!voxel!.TryGetPartition(out TPartition? attachedPartition)
 45762232            || !ReferenceEquals(attachedPartition, partition))
 233        {
 2234            releasePartition(partition);
 2235            return;
 236        }
 237
 45760238        _ = voxel.TryRemovePartition<TPartition>();
 45760239    }
 240}