< Summary

Information
Class: Trailblazer.Pathing.NavigationAStarGuideLease
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Search/AStar/NavigationAStarGuideLease.cs
Line coverage
100%
Covered lines: 140
Uncovered lines: 0
Coverable lines: 140
Total lines: 254
Line coverage: 100%
Branch coverage
100%
Covered branches: 54
Total branches: 54
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Generation()100%11100%
Bind(...)100%11100%
GetStatus(...)100%44100%
GetCurrentWaypointOrdinal(...)100%22100%
GetWaypointCount(...)100%22100%
GetTotalCost(...)100%22100%
TryAdvanceWaypoint(...)100%22100%
TryGetCurrentStep(...)100%22100%
CompletePendingTransition(...)100%1010100%
TryDetach(...)100%22100%
Dispose(...)100%11100%
IsGenerationActiveUnderLock(...)100%66100%
TryGetCurrentStepUnderLock(...)100%44100%
TryAdvanceWaypointUnderLock()100%66100%
ValidateCurrentPayloadUnderLock(...)100%1212100%
MarkStaleUnderLock()100%11100%

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Search/AStar/NavigationAStarGuideLease.cs

#LineLine coverage
 1//=======================================================================
 2// NavigationAStarGuideLease.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System.Diagnostics;
 9using FixedMathSharp;
 10
 11namespace Trailblazer.Pathing;
 12
 13/// <summary>Owns one immutable A* payload and a guide-local waypoint cursor.</summary>
 14internal sealed class NavigationAStarGuideLease
 15{
 16    private readonly NavigationAStarPayloadCache _owner;
 7417    private readonly object _sync = new();
 18    private NavigationWorldGraphStore? _store;
 19    private NavigationAStarPayloadLease? _payloadLease;
 20    private NavigationAStarQueryStatus _status;
 21    private int _currentWaypointOrdinal;
 22    private TraversalMedium _currentMedium;
 23    private long _generation;
 24
 7425    internal NavigationAStarGuideLease(NavigationAStarPayloadCache owner)
 26    {
 7427        _owner = owner;
 7428    }
 29
 30    internal long Generation
 31    {
 189332        get { lock (_sync) return _generation; }
 33    }
 34
 35    internal NavigationAStarGuideLease? NextPooled { get; set; }
 36
 37    internal void Bind(
 38        NavigationWorldGraphStore store,
 39        NavigationAStarPayloadLease payloadLease)
 40    {
 62841        lock (_sync)
 42        {
 43            // The cache rents only detached guides and does not return one to its pool
 44            // until TryDetach has cleared this complete ownership tuple.
 45            Debug.Assert(_store == null && _payloadLease == null);
 62846            _generation = NavigationGenerationCounter.Advance(
 62847                _generation,
 62848                "The A* guide lease generation is exhausted.");
 62849            _store = store;
 62850            _payloadLease = payloadLease;
 62851            _currentWaypointOrdinal = 0;
 62852            _currentMedium = payloadLease.Payload.Key.StartMedium;
 62853            _status = NavigationAStarQueryStatus.Success;
 62854        }
 62855    }
 56
 57    internal NavigationAStarQueryStatus GetStatus(long generation)
 58    {
 5659        lock (_sync)
 60        {
 5661            if (!IsGenerationActiveUnderLock(generation))
 262                return NavigationAStarQueryStatus.Stale;
 5463            return _status == NavigationAStarQueryStatus.Success
 5464                ? ValidateCurrentPayloadUnderLock(out _)
 5465                : _status;
 66        }
 5667    }
 68
 69    internal int GetCurrentWaypointOrdinal(long generation)
 70    {
 9271        lock (_sync)
 9272            return IsGenerationActiveUnderLock(generation) ? _currentWaypointOrdinal : -1;
 9273    }
 74
 75    internal int GetWaypointCount(long generation)
 76    {
 25977        lock (_sync)
 78        {
 25979            return IsGenerationActiveUnderLock(generation)
 25980                ? _payloadLease!.Payload.GuidePoints.Length
 25981                : 0;
 82        }
 25983    }
 84
 85    internal Fixed64 GetTotalCost(long generation)
 86    {
 3787        lock (_sync)
 88        {
 3789            return IsGenerationActiveUnderLock(generation)
 3790                ? _payloadLease!.Payload.Cost
 3791                : Fixed64.Zero;
 92        }
 3793    }
 94
 95    internal NavigationAStarQueryStatus TryAdvanceWaypoint(long generation)
 96    {
 27797        lock (_sync)
 98        {
 27799            return IsGenerationActiveUnderLock(generation)
 277100                ? TryAdvanceWaypointUnderLock()
 277101                : NavigationAStarQueryStatus.Stale;
 102        }
 277103    }
 104
 105    internal NavigationAStarQueryStatus TryGetCurrentStep(
 106        long generation,
 107        out NavigationGuideStep step)
 108    {
 3541109        lock (_sync)
 110        {
 3541111            if (!IsGenerationActiveUnderLock(generation))
 112            {
 3113                step = default;
 3114                return NavigationAStarQueryStatus.Stale;
 115            }
 3538116            return TryGetCurrentStepUnderLock(out step);
 117        }
 3541118    }
 119
 120    internal NavigationAStarQueryStatus CompletePendingTransition(
 121        long generation,
 122        in NavigationTransitionInstruction instruction)
 123    {
 38124        lock (_sync)
 125        {
 38126            if (!IsGenerationActiveUnderLock(generation))
 1127                return NavigationAStarQueryStatus.Stale;
 37128            NavigationAStarQueryStatus status = ValidateCurrentPayloadUnderLock(
 37129                out NavigationAStarPayload payload);
 37130            if (status != NavigationAStarQueryStatus.Success)
 5131                return status;
 32132            NavigationAStarGuidePoint current =
 32133                payload.GuidePoints[_currentWaypointOrdinal];
 32134            if (!current.HasTransition
 32135                || !instruction.MatchesCompletion(
 32136                    this,
 32137                    unchecked((ulong)generation),
 32138                    _currentWaypointOrdinal))
 139            {
 5140                return NavigationAStarQueryStatus.Stale;
 141            }
 27142            _currentMedium = payload
 27143                .TransitionInstructions[current.TransitionOrdinal]
 27144                .DestinationMedium;
 27145            if (_currentWaypointOrdinal + 1 < payload.GuidePoints.Length)
 27146                _currentWaypointOrdinal++;
 27147            return NavigationAStarQueryStatus.Success;
 148        }
 38149    }
 150
 151    internal bool TryDetach(
 152        long generation,
 153        out NavigationAStarPayloadLease payloadLease)
 154    {
 613155        lock (_sync)
 156        {
 613157            payloadLease = null!;
 613158            if (!IsGenerationActiveUnderLock(generation))
 5159                return false;
 608160            _store = null;
 608161            payloadLease = _payloadLease!;
 608162            _payloadLease = null;
 608163            _currentWaypointOrdinal = 0;
 608164            _currentMedium = TraversalMedium.Unknown;
 608165            _status = NavigationAStarQueryStatus.Pending;
 608166            return true;
 167        }
 613168    }
 169
 613170    internal void Dispose(long generation) => _owner.ReturnGuide(this, generation);
 171
 172    private bool IsGenerationActiveUnderLock(long generation) =>
 4913173        generation > 0
 4913174        && generation == _generation
 4913175        && _store != null
 4913176        && _payloadLease != null;
 177
 178    private NavigationAStarQueryStatus TryGetCurrentStepUnderLock(
 179        out NavigationGuideStep step)
 180    {
 3538181        step = default;
 3538182        NavigationAStarQueryStatus status = ValidateCurrentPayloadUnderLock(
 3538183            out NavigationAStarPayload payload);
 3538184        if (status != NavigationAStarQueryStatus.Success)
 3185            return status;
 3535186        NavigationAStarGuidePoint current = payload.GuidePoints[_currentWaypointOrdinal];
 3535187        if (!current.HasTransition)
 188        {
 480189            step = new NavigationGuideStep(
 480190                current.Address,
 480191                current.Position,
 480192                current.Medium,
 480193                default,
 480194                hasTransition: false);
 480195            return NavigationAStarQueryStatus.Success;
 196        }
 3055197        NavigationTransitionInstruction stamped = payload
 3055198            .TransitionInstructions[current.TransitionOrdinal]
 3055199            .WithCompletionStamp(this, unchecked((ulong)_generation), _currentWaypointOrdinal);
 3055200        step = new NavigationGuideStep(
 3055201            current.Address,
 3055202            current.Position,
 3055203            current.Medium,
 3055204            stamped,
 3055205            hasTransition: true);
 3055206        return NavigationAStarQueryStatus.Success;
 207    }
 208
 209    private NavigationAStarQueryStatus TryAdvanceWaypointUnderLock()
 210    {
 276211        NavigationAStarQueryStatus status = ValidateCurrentPayloadUnderLock(
 276212            out NavigationAStarPayload payload);
 276213        if (status != NavigationAStarQueryStatus.Success)
 1214            return status;
 275215        NavigationAStarGuidePoint current = payload.GuidePoints[_currentWaypointOrdinal];
 275216        if (current.HasTransition)
 3217            return NavigationAStarQueryStatus.Pending;
 272218        if (_currentWaypointOrdinal + 1 < payload.GuidePoints.Length)
 265219            _currentWaypointOrdinal++;
 272220        return NavigationAStarQueryStatus.Success;
 221    }
 222
 223    private NavigationAStarQueryStatus ValidateCurrentPayloadUnderLock(
 224        out NavigationAStarPayload payload)
 225    {
 3902226        payload = null!;
 3902227        if (_status != NavigationAStarQueryStatus.Success)
 3228            return _status;
 3899229        NavigationWorldGraphStore store = _store!;
 3899230        NavigationAStarPayloadLease payloadLease = _payloadLease!;
 3899231        NavigationWorldGraphLease? graphLease = store.TryAcquire();
 3899232        if (graphLease == null)
 4233            return NavigationAStarQueryStatus.CapacityExceeded;
 3895234        using (graphLease)
 235        {
 3895236            payload = payloadLease.Payload;
 3895237            if (!graphLease.Graph.IsDependencyCurrent(payload.Dependencies)
 3895238                || !_owner.IsWorldCurrent(payload)
 3895239                || !store.Current.IsDependencyCurrent(payload.Dependencies)
 3895240                || !_owner.IsWorldCurrent(payload))
 241            {
 15242                payload = null!;
 15243                return MarkStaleUnderLock();
 244            }
 3880245            return NavigationAStarQueryStatus.Success;
 246        }
 3895247    }
 248
 249    private NavigationAStarQueryStatus MarkStaleUnderLock()
 250    {
 15251        _status = NavigationAStarQueryStatus.Stale;
 15252        return _status;
 253    }
 254}