< Summary

Information
Class: Trailblazer.Pathing.TraversalTransitionRegistry
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Transition/Registry/TraversalTransitionRegistry.cs
Line coverage
99%
Covered lines: 378
Uncovered lines: 1
Coverable lines: 379
Total lines: 812
Line coverage: 99.7%
Branch coverage
95%
Covered branches: 184
Total branches: 192
Branch coverage: 95.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_State()100%11100%
get__transitions()100%11100%
get__activeTransitionIds()100%11100%
get__suppressedManagedTransitionIds()100%11100%
get__managedManualTransitionIdsByVoxel()100%11100%
get__outgoingTransitionIdsByVoxel()100%11100%
get__incomingTransitionIdsByVoxel()100%11100%
get__transitionIdsBySourceGrid()100%11100%
get__transitionIdsByDestinationGrid()100%11100%
get__transitionLock()100%11100%
get__registrationOrder()100%11100%
set__registrationOrder(...)100%11100%
get__allTransitionsSnapshot()100%11100%
set__allTransitionsSnapshot(...)100%11100%
get_RegistryVersion()100%11100%
get_AllTransitions()100%22100%
Register(...)100%11100%
RegisterGenerated(...)100%11100%
RegisterGeneratedRange(...)100%1616100%
RegisterInternal(...)90.9%2222100%
IsRegistered(...)100%11100%
IsActive(...)100%11100%
TryGet(...)100%22100%
TryGetResolvedEndpoints(...)100%22100%
Unregister(...)100%22100%
UnregisterRange(...)95%2020100%
GetOutgoingTransitions(...)100%11100%
GetIncomingTransitions(...)100%11100%
GetOutgoingTransitions(...)100%22100%
GetIncomingTransitions(...)100%22100%
Reset()100%22100%
GetActiveTransitions()100%11100%
GetActiveTransitionsTouchingGrid(...)100%1212100%
QueryTransitionsByKey(...)100%22100%
AppendTransitions(...)100%44100%
AppendTransitions(...)100%66100%
AddIndexValue(...)100%22100%
BuildSortedSnapshot(...)100%11100%
CheckDuplicateManualTransition_NoLock(...)100%66100%
ShouldPromote(...)100%22100%
RebuildActiveState_NoLock()100%1414100%
TryResolveAnchorVoxelIndex(...)83.33%66100%
IsSuppressed(...)100%11100%
RefreshManagedManualTransitions()100%66100%
RefreshManagedManualTransitionsForVoxel(...)100%1010100%
SetManagedTransitionsSuppressed(...)95.45%2222100%
RollbackRegisteredTransitions_NoLock(...)66.66%66100%
AddManagedManualDependencyIndexes_NoLock(...)100%22100%
RemoveManagedManualDependencyIndexes_NoLock(...)100%22100%
RemoveIndexValue(...)100%44100%
SetManagedTransitionSuppressedState_NoLock(...)100%22100%
IsManagedManualTransitionCurrentlyActive(...)100%22100%
DoesResolvedEndpointSupportMedium(...)87.5%8890%
IsManagedOwnershipKind(...)100%22100%
IsManualOwnershipKind(...)100%11100%

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Transition/Registry/TraversalTransitionRegistry.cs

#LineLine coverage
 1using FixedMathSharp;
 2using GridForge.Grids;
 3using GridForge.Spatial;
 4using SwiftCollections;
 5using System;
 6using System.Threading;
 7
 8namespace Trailblazer.Pathing;
 9
 10/// <summary>
 11/// Default-context facade for authored traversal transitions.
 12/// </summary>
 13/// <remarks>
 14/// Context-owned transition storage lives in <see cref="TraversalTransitionRegistryState"/>.
 15/// Registration resolves transition endpoints to the active pathing context and keeps transitions
 16/// registered even while their resolved endpoints are temporarily unsupported. Manual transitions
 17/// participate in the same active versus suppressed lifecycle model as chart-generated transitions.
 18/// External <see cref="GridWorld"/> add and remove events are reevaluated through
 19/// the owning context's grid-lifecycle bridge, and external world reset is treated as a hard
 20/// reset for that context's pathing state.
 21/// </remarks>
 22internal static class TraversalTransitionRegistry
 23{
 24    /// <summary>
 25    /// Default priority assigned to manual transition registrations.
 26    /// </summary>
 27    public const int DefaultManualPriority = 1;
 28
 4979229    private static TraversalTransitionRegistryState State => PathManager.ActiveState.TransitionRegistryState;
 30
 522731    private static SwiftDictionary<string, RegisteredTraversalTransition> _transitions => State.Transitions;
 32
 220733    private static SwiftHashSet<string> _activeTransitionIds => State.ActiveTransitionIds;
 34
 243735    private static SwiftHashSet<string> _suppressedManagedTransitionIds => State.SuppressedManagedTransitionIds;
 36
 37    private static SwiftDictionary<WorldVoxelIndex, SwiftHashSet<string>> _managedManualTransitionIdsByVoxel =>
 677438        State.ManagedManualTransitionIdsByVoxel;
 39
 40    private static SwiftDictionary<WorldVoxelIndex, SwiftHashSet<string>> _outgoingTransitionIdsByVoxel =>
 219741        State.OutgoingTransitionIdsByVoxel;
 42
 43    private static SwiftDictionary<WorldVoxelIndex, SwiftHashSet<string>> _incomingTransitionIdsByVoxel =>
 218544        State.IncomingTransitionIdsByVoxel;
 45
 46    private static SwiftDictionary<int, SwiftHashSet<string>> _transitionIdsBySourceGrid =>
 230747        State.TransitionIdsBySourceGrid;
 48
 49    private static SwiftDictionary<int, SwiftHashSet<string>> _transitionIdsByDestinationGrid =>
 230750        State.TransitionIdsByDestinationGrid;
 51
 1763452    private static ReaderWriterLockSlim _transitionLock => State.TransitionLock;
 53
 54    private static int _registrationOrder
 55    {
 20956        get => State.RegistrationOrder;
 187457        set => State.RegistrationOrder = value;
 58    }
 59
 60    private static TraversalTransition[] _allTransitionsSnapshot
 61    {
 7462        get => State.AllTransitionsSnapshot;
 189863        set => State.AllTransitionsSnapshot = value;
 64    }
 65
 66    /// <summary>
 67    /// Monotonic version used to invalidate cache keys when transition topology changes.
 68    /// </summary>
 56469    public static int RegistryVersion => State.RegistryVersion;
 70
 71    /// <summary>
 72    /// Returns a snapshot of all currently active transitions after precedence is applied.
 73    /// </summary>
 74    public static TraversalTransition[] AllTransitions
 75    {
 76        get
 77        {
 878            _transitionLock.EnterReadLock();
 79            try
 80            {
 881                TraversalTransition[] snapshot = _allTransitionsSnapshot;
 882                if (snapshot.Length == 0)
 283                    return Array.Empty<TraversalTransition>();
 84
 685                var copy = new TraversalTransition[snapshot.Length];
 686                Array.Copy(snapshot, copy, snapshot.Length);
 687                return copy;
 88            }
 89            finally
 90            {
 891                _transitionLock.ExitReadLock();
 892            }
 893        }
 94    }
 95
 96    /// <summary>
 97    /// Registers a managed manual traversal transition and resolves both endpoints against the active voxel grid.
 98    /// </summary>
 99    /// <param name="transition">The authored transition to register.</param>
 100    /// <param name="priority">
 101    /// The precedence priority used when multiple registered transitions have the same effective semantics.
 102    /// Higher priority wins before registration order ties are considered.
 103    /// </param>
 104    /// <returns>True when the transition is registered; false when the id already exists or either endpoint has no voxe
 105    public static bool Register(TraversalTransition transition, int priority = DefaultManualPriority) =>
 142106        RegisterInternal(
 142107            transition,
 142108            TraversalTransitionOwnershipKind.ManagedManual,
 142109            priority,
 142110            startSuppressed: false);
 111
 112    internal static bool RegisterGenerated(
 113        TraversalTransition transition,
 114        int priority = 0,
 115        bool startSuppressed = false) =>
 8116        RegisterInternal(
 8117            transition,
 8118            TraversalTransitionOwnershipKind.ManagedGenerated,
 8119            priority,
 8120            startSuppressed);
 121
 122    internal static bool RegisterGeneratedRange(
 123        TraversalTransition[] transitions,
 124        int priority,
 125        bool startSuppressed = false)
 126    {
 22127        if (transitions == null)
 1128            throw new ArgumentNullException(nameof(transitions));
 129
 21130        if (transitions.Length == 0)
 1131            return true;
 132
 166133        for (int i = 0; i < transitions.Length; i++)
 134        {
 64135            if (!TryResolveAnchorVoxelIndex(transitions[i].Source, out _)
 64136                || !TryResolveAnchorVoxelIndex(transitions[i].Destination, out _))
 137            {
 1138                return false;
 139            }
 140        }
 141
 19142        _transitionLock.EnterWriteLock();
 143        try
 144        {
 19145            string[] addedIds = new string[transitions.Length];
 19146            int addedCount = 0;
 147
 160148            for (int i = 0; i < transitions.Length; i++)
 149            {
 63150                TraversalTransition transition = transitions[i];
 63151                if (_transitions.ContainsKey(transition.Id))
 152                {
 2153                    RollbackRegisteredTransitions_NoLock(addedIds, addedCount);
 2154                    return false;
 155                }
 156
 61157                var registered = new RegisteredTraversalTransition(
 61158                    transition,
 61159                    TraversalTransitionOwnershipKind.ManagedGenerated,
 61160                    priority,
 61161                    ++_registrationOrder);
 162
 61163                _transitions.Add(transition.Id, registered);
 61164                addedIds[addedCount++] = transition.Id;
 165
 61166                if (startSuppressed)
 60167                    _suppressedManagedTransitionIds.Add(transition.Id);
 168            }
 169
 17170            RebuildActiveState_NoLock();
 17171            State.IncrementRegistryVersion();
 17172            return true;
 173        }
 174        finally
 175        {
 19176            _transitionLock.ExitWriteLock();
 19177        }
 19178    }
 179
 180    private static bool RegisterInternal(
 181        TraversalTransition transition,
 182        TraversalTransitionOwnershipKind ownershipKind,
 183        int priority,
 184        bool startSuppressed)
 185    {
 186        // Validate that both endpoints resolve to voxels in the active grid setup before acquiring the lock.
 150187        if (!TryResolveAnchorVoxelIndex(transition.Source, out _)
 150188            || !TryResolveAnchorVoxelIndex(transition.Destination, out _))
 189        {
 1190            return false;
 191        }
 192
 149193        _transitionLock.EnterWriteLock();
 194        try
 195        {
 149196            if (_transitions.ContainsKey(transition.Id))
 1197                return false;
 198
 148199            var registered = new RegisteredTraversalTransition(
 148200                transition,
 148201                ownershipKind,
 148202                priority,
 148203                ++_registrationOrder);
 204
 148205            if (IsManualOwnershipKind(ownershipKind)
 148206                && CheckDuplicateManualTransition_NoLock(registered))
 207            {
 1208                TrailblazerLogger.Channel.Warn(
 1209                    $"Ignored duplicate manual traversal transition '{transition.Id}' because it matches an existing man
 1210                return false;
 211            }
 212
 147213            _transitions.Add(transition.Id, registered);
 147214            if (IsManualOwnershipKind(ownershipKind))
 139215                AddManagedManualDependencyIndexes_NoLock(registered);
 216
 147217            if ((startSuppressed && IsManagedOwnershipKind(ownershipKind))
 147218                || (ownershipKind == TraversalTransitionOwnershipKind.ManagedManual
 147219                    && !IsManagedManualTransitionCurrentlyActive(registered)))
 220            {
 7221                _suppressedManagedTransitionIds.Add(transition.Id);
 222            }
 223
 147224            RebuildActiveState_NoLock();
 147225            State.IncrementRegistryVersion();
 147226            return true;
 227        }
 228        finally
 229        {
 149230            _transitionLock.ExitWriteLock();
 149231        }
 149232    }
 233
 234    /// <summary>
 235    /// Returns true if a transition with the provided id exists.
 236    /// </summary>
 237    public static bool IsRegistered(string id)
 238    {
 51239        _transitionLock.EnterReadLock();
 51240        try { return _transitions.ContainsKey(id); }
 102241        finally { _transitionLock.ExitReadLock(); }
 51242    }
 243
 244    /// <summary>
 245    /// Returns true if the transition is currently active for query purposes after precedence is applied.
 246    /// </summary>
 247    public static bool IsActive(string id)
 248    {
 32249        _transitionLock.EnterReadLock();
 32250        try { return _activeTransitionIds.Contains(id); }
 64251        finally { _transitionLock.ExitReadLock(); }
 32252    }
 253
 254    /// <summary>
 255    /// Attempts to retrieve a registered transition by id, even if it is currently inactive.
 256    /// </summary>
 257    public static bool TryGet(string id, out TraversalTransition transition)
 258    {
 18259        _transitionLock.EnterReadLock();
 260        try
 261        {
 18262            if (_transitions.TryGetValue(id, out RegisteredTraversalTransition registered))
 263            {
 17264                transition = registered.Transition;
 17265                return true;
 266            }
 267
 1268            transition = default;
 1269            return false;
 270        }
 271        finally
 272        {
 18273            _transitionLock.ExitReadLock();
 18274        }
 18275    }
 276
 277    /// <summary>
 278    /// Attempts to retrieve the voxel indices resolved for a registered transition, even if it is currently inactive.
 279    /// </summary>
 280    public static bool TryGetResolvedEndpoints(
 281        string id,
 282        out WorldVoxelIndex sourceVoxelIndex,
 283        out WorldVoxelIndex destinationVoxelIndex)
 284    {
 3285        _transitionLock.EnterReadLock();
 286        try
 287        {
 3288            if (_transitions.TryGetValue(id, out RegisteredTraversalTransition registered))
 289            {
 2290                sourceVoxelIndex = registered.SourceVoxelIndex;
 2291                destinationVoxelIndex = registered.DestinationVoxelIndex;
 2292                return true;
 293            }
 294
 1295            sourceVoxelIndex = default;
 1296            destinationVoxelIndex = default;
 1297            return false;
 298        }
 299        finally
 300        {
 3301            _transitionLock.ExitReadLock();
 3302        }
 3303    }
 304
 305    /// <summary>
 306    /// Removes a transition by id.
 307    /// </summary>
 308    public static bool Unregister(string id)
 309    {
 7310        _transitionLock.EnterWriteLock();
 311        try
 312        {
 7313            if (!_transitions.TryGetValue(id, out RegisteredTraversalTransition registered))
 1314                return false;
 315
 6316            _transitions.Remove(id);
 6317            RemoveManagedManualDependencyIndexes_NoLock(registered);
 6318            _suppressedManagedTransitionIds.Remove(id);
 6319            RebuildActiveState_NoLock();
 6320            State.IncrementRegistryVersion();
 6321            return true;
 322        }
 14323        finally { _transitionLock.ExitWriteLock(); }
 7324    }
 325
 326    internal static void UnregisterRange(string[] ids, int count = -1)
 327    {
 205328        if (ids == null || ids.Length == 0)
 196329            return;
 330
 9331        int unregisterCount = count < 0 || count > ids.Length
 9332            ? ids.Length
 9333            : count;
 9334        if (unregisterCount == 0)
 1335            return;
 336
 8337        _transitionLock.EnterWriteLock();
 338        try
 339        {
 8340            bool removedAny = false;
 48341            for (int i = 0; i < unregisterCount; i++)
 342            {
 16343                string id = ids[i];
 16344                if (string.IsNullOrEmpty(id))
 345                    continue;
 346
 16347                if (!_transitions.TryGetValue(id, out RegisteredTraversalTransition registered))
 348                    continue;
 349
 14350                if (_transitions.Remove(id))
 351                {
 14352                    RemoveManagedManualDependencyIndexes_NoLock(registered);
 14353                    _suppressedManagedTransitionIds.Remove(id);
 14354                    removedAny = true;
 355                }
 356            }
 357
 8358            if (!removedAny)
 2359                return;
 360
 6361            RebuildActiveState_NoLock();
 6362            State.IncrementRegistryVersion();
 6363        }
 16364        finally { _transitionLock.ExitWriteLock(); }
 8365    }
 366
 367    /// <summary>
 368    /// Returns the currently active transitions whose authored source anchor resolves to the provided voxel.
 369    /// </summary>
 370    public static TraversalTransition[] GetOutgoingTransitions(WorldVoxelIndex sourceVoxelIndex) =>
 22371        QueryTransitionsByKey(_outgoingTransitionIdsByVoxel, sourceVoxelIndex);
 372
 373    /// <summary>
 374    /// Returns the currently active transitions whose authored destination anchor resolves to the provided voxel.
 375    /// </summary>
 376    public static TraversalTransition[] GetIncomingTransitions(WorldVoxelIndex destinationVoxelIndex) =>
 10377        QueryTransitionsByKey(_incomingTransitionIdsByVoxel, destinationVoxelIndex);
 378
 379    /// <summary>
 380    /// Resolves the world position to a voxel and returns outgoing transitions from that voxel.
 381    /// </summary>
 382    public static TraversalTransition[] GetOutgoingTransitions(Vector3d sourcePosition)
 383    {
 22384        if (!TraversalTransitionAnchor.TryResolveVoxelIndex(sourcePosition, out WorldVoxelIndex sourceVoxelIndex))
 1385            return Array.Empty<TraversalTransition>();
 386
 21387        return GetOutgoingTransitions(sourceVoxelIndex);
 388    }
 389
 390    /// <summary>
 391    /// Resolves the world position to a voxel and returns incoming transitions to that voxel.
 392    /// </summary>
 393    public static TraversalTransition[] GetIncomingTransitions(Vector3d destinationPosition)
 394    {
 10395        if (!TraversalTransitionAnchor.TryResolveVoxelIndex(destinationPosition, out WorldVoxelIndex destinationVoxelInd
 1396            return Array.Empty<TraversalTransition>();
 397
 9398        return GetIncomingTransitions(destinationVoxelIndex);
 399    }
 400
 401    internal static void Reset()
 402    {
 1700403        if (!PathManager.TryGetActiveState(out _))
 35404            return;
 405
 1665406        _transitionLock.EnterWriteLock();
 407        try
 408        {
 1665409            _transitions.Clear();
 1665410            _activeTransitionIds.Clear();
 1665411            _suppressedManagedTransitionIds.Clear();
 1665412            _managedManualTransitionIdsByVoxel.Clear();
 1665413            _outgoingTransitionIdsByVoxel.Clear();
 1665414            _incomingTransitionIdsByVoxel.Clear();
 1665415            _transitionIdsBySourceGrid.Clear();
 1665416            _transitionIdsByDestinationGrid.Clear();
 1665417            _registrationOrder = 0;
 1665418            _allTransitionsSnapshot = Array.Empty<TraversalTransition>();
 1665419            State.IncrementRegistryVersion();
 1665420        }
 3330421        finally { _transitionLock.ExitWriteLock(); }
 1665422    }
 423
 424    internal static TraversalTransition[] GetActiveTransitions()
 425    {
 66426        _transitionLock.EnterReadLock();
 427        try
 428        {
 66429            return _allTransitionsSnapshot;
 430        }
 431        finally
 432        {
 66433            _transitionLock.ExitReadLock();
 66434        }
 66435    }
 436
 437    internal static TraversalTransition[] GetActiveTransitionsTouchingGrid(int gridIndex)
 438    {
 132439        _transitionLock.EnterReadLock();
 440        try
 441        {
 132442            bool hasSource = _transitionIdsBySourceGrid.TryGetValue(gridIndex, out SwiftHashSet<string> sourceIds);
 132443            bool hasDestination = _transitionIdsByDestinationGrid.TryGetValue(gridIndex, out SwiftHashSet<string> destin
 132444            if (!hasSource && !hasDestination)
 12445                return Array.Empty<TraversalTransition>();
 446
 120447            int capacity = (hasSource ? sourceIds.Count : 0) + (hasDestination ? destinationIds.Count : 0);
 120448            SwiftList<TraversalTransition> result = new(capacity);
 120449            SwiftHashSet<string> seenIds = new();
 450
 120451            if (hasSource)
 117452                AppendTransitions(result, seenIds, sourceIds);
 453
 120454            if (hasDestination)
 117455                AppendTransitions(result, seenIds, destinationIds);
 456
 120457            return BuildSortedSnapshot(result);
 458        }
 459        finally
 460        {
 132461            _transitionLock.ExitReadLock();
 132462        }
 132463    }
 464
 465    private static TraversalTransition[] QueryTransitionsByKey<TKey>(
 466        SwiftDictionary<TKey, SwiftHashSet<string>> index,
 467        TKey key)
 468        where TKey : notnull
 469    {
 32470        _transitionLock.EnterReadLock();
 471        try
 472        {
 32473            if (!index.TryGetValue(key, out SwiftHashSet<string> transitionIds))
 8474                return Array.Empty<TraversalTransition>();
 475
 24476            SwiftList<TraversalTransition> result = new(transitionIds.Count);
 24477            AppendTransitions(result, transitionIds);
 24478            return BuildSortedSnapshot(result);
 479        }
 480        finally
 481        {
 32482            _transitionLock.ExitReadLock();
 32483        }
 32484    }
 485
 486    private static void AppendTransitions(
 487        SwiftList<TraversalTransition> destination,
 488        SwiftHashSet<string> transitionIds)
 489    {
 98490        foreach (string transitionId in transitionIds)
 491        {
 25492            if (_transitions.TryGetValue(transitionId, out RegisteredTraversalTransition registered))
 25493                destination.Add(registered.Transition);
 494        }
 24495    }
 496
 497    private static void AppendTransitions(
 498        SwiftList<TraversalTransition> destination,
 499        SwiftHashSet<string> seenIds,
 500        SwiftHashSet<string> transitionIds)
 501    {
 1486502        foreach (string transitionId in transitionIds)
 503        {
 509504            if (!seenIds.Add(transitionId))
 505                continue;
 506
 258507            if (_transitions.TryGetValue(transitionId, out RegisteredTraversalTransition registered))
 258508                destination.Add(registered.Transition);
 509        }
 234510    }
 511
 512    private static void AddIndexValue<TKey>(
 513        SwiftDictionary<TKey, SwiftHashSet<string>> index,
 514        TKey key,
 515        string transitionId)
 516        where TKey : notnull
 517    {
 1352518        if (!index.TryGetValue(key, out SwiftHashSet<string> transitionIds))
 519        {
 1085520            transitionIds = new SwiftHashSet<string>();
 1085521            index.Add(key, transitionIds);
 522        }
 523
 1352524        transitionIds.Add(transitionId);
 1352525    }
 526
 527    private static TraversalTransition[] BuildSortedSnapshot(SwiftList<TraversalTransition> transitions)
 528    {
 370529        TraversalTransition[] snapshot = transitions.ToArray();
 370530        TraversalTransitionOrdering.Sort(snapshot);
 370531        return snapshot;
 532    }
 533
 534    private static bool CheckDuplicateManualTransition_NoLock(RegisteredTraversalTransition duplicate)
 535    {
 383536        foreach (RegisteredTraversalTransition registered in _transitions.Values)
 537        {
 52538            if (IsManualOwnershipKind(registered.OwnershipKind)
 52539                && registered.Equals(duplicate))
 540            {
 1541                return true;
 542            }
 543        }
 544
 139545        return false;
 1546    }
 547
 548    private static bool ShouldPromote(
 549        RegisteredTraversalTransition candidate,
 550        RegisteredTraversalTransition current)
 551    {
 5552        if (candidate.Priority != current.Priority)
 4553            return candidate.Priority > current.Priority;
 554
 1555        return candidate.RegistrationOrder < current.RegistrationOrder;
 556    }
 557
 558    private static void RebuildActiveState_NoLock()
 559    {
 233560        _activeTransitionIds.Clear();
 233561        _outgoingTransitionIdsByVoxel.Clear();
 233562        _incomingTransitionIdsByVoxel.Clear();
 233563        _transitionIdsBySourceGrid.Clear();
 233564        _transitionIdsByDestinationGrid.Clear();
 565
 233566        if (_transitions.Count == 0)
 567        {
 7568            _allTransitionsSnapshot = Array.Empty<TraversalTransition>();
 7569            return;
 570        }
 571
 226572        SwiftHashSet<RegisteredTraversalTransition> activeByIdentity = new(_transitions.Count);
 1236573        foreach (RegisteredTraversalTransition registered in _transitions.Values)
 574        {
 392575            if (IsManagedOwnershipKind(registered.OwnershipKind)
 392576                && _suppressedManagedTransitionIds.Contains(registered.Transition.Id))
 577            {
 578                continue;
 579            }
 580
 282581            if (!activeByIdentity.TryGetValue(registered, out RegisteredTraversalTransition current))
 277582                activeByIdentity.Add(registered);
 5583            else if (ShouldPromote(registered, current))
 584            {
 3585                activeByIdentity.Remove(current);
 3586                activeByIdentity.Add(registered);
 587            }
 588        }
 589
 226590        SwiftList<TraversalTransition> activeTransitions = new(activeByIdentity.Count);
 1006591        foreach (RegisteredTraversalTransition registered in activeByIdentity)
 592        {
 277593            _activeTransitionIds.Add(registered.Transition.Id);
 277594            AddIndexValue(_outgoingTransitionIdsByVoxel, registered.SourceVoxelIndex, registered.Transition.Id);
 277595            AddIndexValue(_incomingTransitionIdsByVoxel, registered.DestinationVoxelIndex, registered.Transition.Id);
 277596            AddIndexValue(_transitionIdsBySourceGrid, registered.SourceVoxelIndex.GridIndex, registered.Transition.Id);
 277597            AddIndexValue(_transitionIdsByDestinationGrid, registered.DestinationVoxelIndex.GridIndex, registered.Transi
 277598            activeTransitions.Add(registered.Transition);
 599        }
 600
 226601        _allTransitionsSnapshot = BuildSortedSnapshot(activeTransitions);
 226602    }
 603
 604    private static bool TryResolveAnchorVoxelIndex(
 605        TraversalTransitionAnchor anchor,
 606        out WorldVoxelIndex voxelIndex)
 607    {
 427608        voxelIndex = anchor.VoxelIndex;
 427609        GridWorld world = PathManager.ActiveState.World;
 427610        if (!world.TryGetGridAndVoxel(voxelIndex, out _, out _))
 2611            return false;
 612
 425613        if (!anchor.HasPointOverride)
 421614            return true;
 615
 4616        return TraversalTransitionAnchor.TryResolveVoxelIndex(
 4617                world,
 4618                anchor.PointOverride,
 4619                out WorldVoxelIndex pointOverrideVoxelIndex)
 4620            && pointOverrideVoxelIndex == voxelIndex;
 621    }
 622
 623    internal static bool IsSuppressed(string id)
 624    {
 2625        _transitionLock.EnterReadLock();
 2626        try { return _suppressedManagedTransitionIds.Contains(id); }
 4627        finally { _transitionLock.ExitReadLock(); }
 2628    }
 629
 630    internal static void RefreshManagedManualTransitions()
 631    {
 1776632        _transitionLock.EnterWriteLock();
 633        try
 634        {
 1776635            bool changed = false;
 3968636            foreach (RegisteredTraversalTransition registered in _transitions.Values)
 637            {
 208638                if (registered.OwnershipKind != TraversalTransitionOwnershipKind.ManagedManual)
 639                    continue;
 640
 152641                changed |= SetManagedTransitionSuppressedState_NoLock(
 152642                    registered.Transition.Id,
 152643                    suppress: !IsManagedManualTransitionCurrentlyActive(registered));
 644            }
 645
 1776646            if (!changed)
 1775647                return;
 648
 1649            RebuildActiveState_NoLock();
 1650            State.IncrementRegistryVersion();
 1651        }
 652        finally
 653        {
 1776654            _transitionLock.ExitWriteLock();
 1776655        }
 1776656    }
 657
 658    internal static void RefreshManagedManualTransitionsForVoxel(WorldVoxelIndex voxelIndex)
 659    {
 4822660        _transitionLock.EnterWriteLock();
 661        try
 662        {
 4822663            if (!_managedManualTransitionIdsByVoxel.TryGetValue(voxelIndex, out SwiftHashSet<string> transitionIds))
 4773664                return;
 665
 49666            bool changed = false;
 222667            foreach (string transitionId in transitionIds)
 668            {
 62669                if (!_transitions.TryGetValue(transitionId, out RegisteredTraversalTransition registered)
 62670                    || registered.OwnershipKind != TraversalTransitionOwnershipKind.ManagedManual)
 671                {
 672                    continue;
 673                }
 674
 62675                changed |= SetManagedTransitionSuppressedState_NoLock(
 62676                    transitionId,
 62677                    suppress: !IsManagedManualTransitionCurrentlyActive(registered));
 678            }
 679
 49680            if (!changed)
 13681                return;
 682
 36683            RebuildActiveState_NoLock();
 36684            State.IncrementRegistryVersion();
 36685        }
 686        finally
 687        {
 4822688            _transitionLock.ExitWriteLock();
 4822689        }
 4822690    }
 691
 692    internal static void SetManagedTransitionsSuppressed(string[] ids, bool suppressed, int count = -1)
 693    {
 29694        if (ids == null || ids.Length == 0)
 1695            return;
 696
 28697        int changeCount = count < 0 || count > ids.Length
 28698            ? ids.Length
 28699            : count;
 28700        if (changeCount == 0)
 1701            return;
 702
 27703        _transitionLock.EnterWriteLock();
 704        try
 705        {
 27706            bool changed = false;
 212707            for (int i = 0; i < changeCount; i++)
 708            {
 79709                string id = ids[i];
 79710                if (string.IsNullOrEmpty(id)
 79711                    || !_transitions.TryGetValue(id, out RegisteredTraversalTransition registered)
 79712                    || !IsManagedOwnershipKind(registered.OwnershipKind))
 713                {
 714                    continue;
 715                }
 716
 75717                changed |= suppressed
 75718                    ? _suppressedManagedTransitionIds.Add(id)
 75719                    : _suppressedManagedTransitionIds.Remove(id);
 720            }
 721
 27722            if (!changed)
 7723                return;
 724
 20725            RebuildActiveState_NoLock();
 20726            State.IncrementRegistryVersion();
 20727        }
 728        finally
 729        {
 27730            _transitionLock.ExitWriteLock();
 27731        }
 27732    }
 733
 734    private static void RollbackRegisteredTransitions_NoLock(string[] ids, int count)
 735    {
 8736        for (int i = 0; i < count; i++)
 737        {
 2738            string id = ids[i];
 2739            if (string.IsNullOrEmpty(id))
 740                continue;
 741
 2742            if (_transitions.TryGetValue(id, out RegisteredTraversalTransition registered))
 2743                RemoveManagedManualDependencyIndexes_NoLock(registered);
 744
 2745            _transitions.Remove(id);
 2746            _suppressedManagedTransitionIds.Remove(id);
 747        }
 2748    }
 749
 750    private static void AddManagedManualDependencyIndexes_NoLock(RegisteredTraversalTransition registered)
 751    {
 139752        AddIndexValue(_managedManualTransitionIdsByVoxel, registered.SourceVoxelIndex, registered.Transition.Id);
 139753        if (!registered.SourceVoxelIndex.Equals(registered.DestinationVoxelIndex))
 105754            AddIndexValue(_managedManualTransitionIdsByVoxel, registered.DestinationVoxelIndex, registered.Transition.Id
 139755    }
 756
 757    private static void RemoveManagedManualDependencyIndexes_NoLock(RegisteredTraversalTransition registered)
 758    {
 22759        RemoveIndexValue(_managedManualTransitionIdsByVoxel, registered.SourceVoxelIndex, registered.Transition.Id);
 22760        if (!registered.SourceVoxelIndex.Equals(registered.DestinationVoxelIndex))
 21761            RemoveIndexValue(_managedManualTransitionIdsByVoxel, registered.DestinationVoxelIndex, registered.Transition
 22762    }
 763
 764    private static void RemoveIndexValue<TKey>(
 765        SwiftDictionary<TKey, SwiftHashSet<string>> index,
 766        TKey key,
 767        string transitionId)
 768        where TKey : notnull
 769    {
 43770        if (!index.TryGetValue(key, out SwiftHashSet<string> transitionIds))
 24771            return;
 772
 19773        transitionIds.Remove(transitionId);
 19774        if (transitionIds.Count == 0)
 9775            index.Remove(key);
 19776    }
 777
 778    private static bool SetManagedTransitionSuppressedState_NoLock(string id, bool suppress)
 779    {
 214780        return suppress
 214781            ? _suppressedManagedTransitionIds.Add(id)
 214782            : _suppressedManagedTransitionIds.Remove(id);
 783    }
 784
 785    private static bool IsManagedManualTransitionCurrentlyActive(RegisteredTraversalTransition registered)
 786    {
 353787        return DoesResolvedEndpointSupportMedium(registered.SourceVoxelIndex, registered.Transition.Source.Medium)
 353788            && DoesResolvedEndpointSupportMedium(registered.DestinationVoxelIndex, registered.Transition.Destination.Med
 789    }
 790
 791    private static bool DoesResolvedEndpointSupportMedium(WorldVoxelIndex voxelIndex, TraversalMedium medium)
 792    {
 640793        if (!PathManager.ActiveState.World.TryGetGridAndVoxel(voxelIndex, out _, out Voxel? voxel)
 640794            || voxel == null)
 1795            return false;
 796
 639797        return medium switch
 639798        {
 466799            TraversalMedium.Solid => voxel.TryGetPartition(out SolidChartPartition? _),
 65800            TraversalMedium.Gas => VolumeMediumRules.Matches(voxel, TraversalMedium.Gas),
 108801            TraversalMedium.Liquid => VolumeMediumRules.Matches(voxel, TraversalMedium.Liquid),
 0802            _ => false
 639803        };
 804    }
 805
 806    private static bool IsManagedOwnershipKind(TraversalTransitionOwnershipKind ownershipKind) =>
 468807        ownershipKind == TraversalTransitionOwnershipKind.ManagedManual
 468808        || ownershipKind == TraversalTransitionOwnershipKind.ManagedGenerated;
 809
 810    private static bool IsManualOwnershipKind(TraversalTransitionOwnershipKind ownershipKind) =>
 347811        ownershipKind == TraversalTransitionOwnershipKind.ManagedManual;
 812}

Methods/Properties

get_State()
get__transitions()
get__activeTransitionIds()
get__suppressedManagedTransitionIds()
get__managedManualTransitionIdsByVoxel()
get__outgoingTransitionIdsByVoxel()
get__incomingTransitionIdsByVoxel()
get__transitionIdsBySourceGrid()
get__transitionIdsByDestinationGrid()
get__transitionLock()
get__registrationOrder()
set__registrationOrder(System.Int32)
get__allTransitionsSnapshot()
set__allTransitionsSnapshot(Trailblazer.Pathing.TraversalTransition[])
get_RegistryVersion()
get_AllTransitions()
Register(Trailblazer.Pathing.TraversalTransition,System.Int32)
RegisterGenerated(Trailblazer.Pathing.TraversalTransition,System.Int32,System.Boolean)
RegisterGeneratedRange(Trailblazer.Pathing.TraversalTransition[],System.Int32,System.Boolean)
RegisterInternal(Trailblazer.Pathing.TraversalTransition,Trailblazer.Pathing.TraversalTransitionOwnershipKind,System.Int32,System.Boolean)
IsRegistered(System.String)
IsActive(System.String)
TryGet(System.String,Trailblazer.Pathing.TraversalTransition&)
TryGetResolvedEndpoints(System.String,GridForge.Spatial.WorldVoxelIndex&,GridForge.Spatial.WorldVoxelIndex&)
Unregister(System.String)
UnregisterRange(System.String[],System.Int32)
GetOutgoingTransitions(GridForge.Spatial.WorldVoxelIndex)
GetIncomingTransitions(GridForge.Spatial.WorldVoxelIndex)
GetOutgoingTransitions(FixedMathSharp.Vector3d)
GetIncomingTransitions(FixedMathSharp.Vector3d)
Reset()
GetActiveTransitions()
GetActiveTransitionsTouchingGrid(System.Int32)
QueryTransitionsByKey(SwiftCollections.SwiftDictionary`2<TKey,SwiftCollections.SwiftHashSet`1<System.String>>,TKey)
AppendTransitions(SwiftCollections.SwiftList`1<Trailblazer.Pathing.TraversalTransition>,SwiftCollections.SwiftHashSet`1<System.String>)
AppendTransitions(SwiftCollections.SwiftList`1<Trailblazer.Pathing.TraversalTransition>,SwiftCollections.SwiftHashSet`1<System.String>,SwiftCollections.SwiftHashSet`1<System.String>)
AddIndexValue(SwiftCollections.SwiftDictionary`2<TKey,SwiftCollections.SwiftHashSet`1<System.String>>,TKey,System.String)
BuildSortedSnapshot(SwiftCollections.SwiftList`1<Trailblazer.Pathing.TraversalTransition>)
CheckDuplicateManualTransition_NoLock(Trailblazer.Pathing.RegisteredTraversalTransition)
ShouldPromote(Trailblazer.Pathing.RegisteredTraversalTransition,Trailblazer.Pathing.RegisteredTraversalTransition)
RebuildActiveState_NoLock()
TryResolveAnchorVoxelIndex(Trailblazer.Pathing.TraversalTransitionAnchor,GridForge.Spatial.WorldVoxelIndex&)
IsSuppressed(System.String)
RefreshManagedManualTransitions()
RefreshManagedManualTransitionsForVoxel(GridForge.Spatial.WorldVoxelIndex)
SetManagedTransitionsSuppressed(System.String[],System.Boolean,System.Int32)
RollbackRegisteredTransitions_NoLock(System.String[],System.Int32)
AddManagedManualDependencyIndexes_NoLock(Trailblazer.Pathing.RegisteredTraversalTransition)
RemoveManagedManualDependencyIndexes_NoLock(Trailblazer.Pathing.RegisteredTraversalTransition)
RemoveIndexValue(SwiftCollections.SwiftDictionary`2<TKey,SwiftCollections.SwiftHashSet`1<System.String>>,TKey,System.String)
SetManagedTransitionSuppressedState_NoLock(System.String,System.Boolean)
IsManagedManualTransitionCurrentlyActive(Trailblazer.Pathing.RegisteredTraversalTransition)
DoesResolvedEndpointSupportMedium(GridForge.Spatial.WorldVoxelIndex,Trailblazer.TraversalMedium)
IsManagedOwnershipKind(Trailblazer.Pathing.TraversalTransitionOwnershipKind)
IsManualOwnershipKind(Trailblazer.Pathing.TraversalTransitionOwnershipKind)