< Summary

Information
Class: Trailblazer.Navigation.MovementGroups.MovementGroupCoordinatorState
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Navigation/MovementGroups/MovementGroupCoordinatorState.cs
Line coverage
99%
Covered lines: 112
Uncovered lines: 1
Coverable lines: 113
Total lines: 260
Line coverage: 99.1%
Branch coverage
94%
Covered branches: 64
Total branches: 68
Branch coverage: 94.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%22100%
get_FrameCount()100%11100%
get_VoxelSize()100%11100%
CacheOwner(...)100%66100%
Prewarm(...)100%22100%
UpdateTarget(...)90%101094.11%
Remove(...)91.66%1212100%
IsNeighbor(...)100%88100%
Reset()100%11100%
GetOrCreateGroup(...)100%22100%
GetOrCreateMember(...)100%22100%
UpdateMemberState(...)100%22100%
UpdateMembership(...)100%66100%
TryGetFormationMetrics(...)100%66100%
UpdateFormationOffsets(...)100%88100%
IsEligibleGroupMember(...)50%22100%

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Navigation/MovementGroups/MovementGroupCoordinatorState.cs

#LineLine coverage
 1using FixedMathSharp;
 2using SwiftCollections;
 3using System;
 4using System.Runtime.CompilerServices;
 5
 6namespace Trailblazer.Navigation.MovementGroups;
 7
 8/// <summary>
 9/// Tracks movement-group membership and resolves formation-preserving destinations for one world context.
 10/// </summary>
 11internal sealed class MovementGroupCoordinatorState
 12{
 13    private const int MinMovementGroupSize = 2;
 14
 15    private const int MovementGroupHistoryFrames = 1;
 16
 17    private readonly TrailblazerWorldContext _context;
 18
 96119    private readonly SwiftDictionary<int, MovementGroupState> _movementGroups = new();
 20
 96121    private readonly SwiftDictionary<Guid, MovementGroupMembership> _movementGroupMemberships = new();
 22
 96123    internal MovementGroupCoordinatorState(TrailblazerWorldContext context)
 24    {
 96125        _context = context ?? throw new ArgumentNullException(nameof(context));
 96126    }
 27
 21028    private int FrameCount => _context.FrameCount;
 29
 2830    private Fixed64 VoxelSize => _context.VoxelSize;
 31
 32    internal void CacheOwner(MovementGroupSession session, Guid ownerId)
 33    {
 6034        if (session.HasOwnerId && session.OwnerId == ownerId)
 835            return;
 36
 5237        if (session.HasOwnerId)
 138            _movementGroupMemberships.Remove(session.OwnerId);
 39
 5240        session.OwnerId = ownerId;
 5241        session.HasOwnerId = true;
 5242    }
 43
 44    internal void Prewarm(
 45        MovementGroupSession session,
 46        Guid ownerId,
 47        Vector3d requestedDestination,
 48        Vector3d position,
 49        Fixed64 radius)
 50    {
 1251        if (session.GroupId < 0)
 152            return;
 53
 1154        CacheOwner(session, ownerId);
 1155        UpdateTarget(session, requestedDestination, position, radius);
 1156    }
 57
 58    internal MovementGroupTarget UpdateTarget(
 59        MovementGroupSession session,
 60        Vector3d requestedDestination,
 61        Vector3d position,
 62        Fixed64 radius,
 63        bool resetFormationOffset = false)
 64    {
 10565        if (session.GroupId < 0)
 066            return new(MovementGroupTravelMode.None, requestedDestination);
 67
 10568        MovementGroupState group = GetOrCreateGroup(session.GroupId);
 10569        MovementGroupMember self = GetOrCreateMember(session, group, ref resetFormationOffset);
 70
 10571        if (self.RequestedDestination != requestedDestination)
 6572            resetFormationOffset = true;
 73
 10574        UpdateMemberState(self, requestedDestination, position, radius, resetFormationOffset);
 10575        UpdateMembership(session, self, requestedDestination);
 76
 10577        int minFrame = FrameCount - MovementGroupHistoryFrames;
 10578        if (!TryGetFormationMetrics(group, requestedDestination, minFrame, out Vector3d groupCenter, out Fixed64 average
 7779            return new(MovementGroupTravelMode.Individual, requestedDestination);
 80
 2881        Fixed64 maxSpreadSq = UpdateFormationOffsets(group, requestedDestination, minFrame, groupCenter);
 2882        Fixed64 allowedSpreadSq = averageRadius * averageRadius * (Fixed64)(groupCount * 2);
 2883        Fixed64 distanceToSharedDestinationSq = (requestedDestination - groupCenter).SqrMagnitude;
 2884        if (maxSpreadSq > allowedSpreadSq || distanceToSharedDestinationSq <= maxSpreadSq)
 585            return new(MovementGroupTravelMode.GroupIndividual, requestedDestination);
 86
 2387        return new(MovementGroupTravelMode.Formation, requestedDestination + self.FormationOffset);
 88    }
 89
 90    internal void Remove(MovementGroupSession session)
 91    {
 32992        if (session.GroupId < 0)
 31893            return;
 94
 1195        if (_movementGroups.TryGetValue(session.GroupId, out MovementGroupState group))
 96        {
 1097            if (group.Members.TryGetValue(session.GroupIndex, out MovementGroupMember member) && member.HasOccupantId)
 198                _movementGroupMemberships.Remove(member.OccupantId);
 99
 10100            group.Members.TryRemoveAt(session.GroupIndex);
 10101            if (group.Members.Count == 0)
 4102                _movementGroups.Remove(session.GroupId);
 103        }
 1104        else if (session.HasOwnerId)
 105        {
 1106            _movementGroupMemberships.Remove(session.OwnerId);
 107        }
 108
 11109        session.GroupIndex = -1;
 11110    }
 111
 112    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 113    internal bool IsNeighbor(
 114        MovementGroupSession session,
 115        Guid otherId,
 116        Vector3d requestedDestination,
 117        int currentFrame)
 118    {
 17119        if (session.GroupId < 0 || !_movementGroupMemberships.TryGetValue(otherId, out MovementGroupMembership membershi
 7120            return false;
 121
 10122        return membership.GroupId == session.GroupId
 10123            && membership.RequestedDestination == requestedDestination
 10124            && membership.LastSeenFrame >= currentFrame - MovementGroupHistoryFrames;
 125    }
 126
 127    internal void Reset()
 128    {
 35129        _movementGroups.Clear();
 35130        _movementGroupMemberships.Clear();
 35131    }
 132
 133    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 134    private MovementGroupState GetOrCreateGroup(int groupId)
 135    {
 105136        if (_movementGroups.TryGetValue(groupId, out MovementGroupState group))
 63137            return group;
 138
 42139        group = new();
 42140        _movementGroups[groupId] = group;
 42141        return group;
 142    }
 143
 144    private static MovementGroupMember GetOrCreateMember(
 145        MovementGroupSession session,
 146        MovementGroupState group,
 147        ref bool resetFormationOffset)
 148    {
 105149        if (group.Members.TryGetValue(session.GroupIndex, out MovementGroupMember self))
 40150            return self;
 151
 65152        self = new();
 65153        session.GroupIndex = group.Members.Add(self);
 65154        resetFormationOffset = true;
 65155        return self;
 156    }
 157
 158    private void UpdateMemberState(
 159        MovementGroupMember self,
 160        Vector3d requestedDestination,
 161        Vector3d position,
 162        Fixed64 radius,
 163        bool resetFormationOffset)
 164    {
 105165        self.Position = position;
 105166        self.Radius = radius;
 105167        self.RequestedDestination = requestedDestination;
 105168        self.LastSeenFrame = FrameCount;
 169
 105170        if (resetFormationOffset)
 65171            self.HasFormationOffset = false;
 105172    }
 173
 174    private void UpdateMembership(
 175        MovementGroupSession session,
 176        MovementGroupMember self,
 177        Vector3d requestedDestination)
 178    {
 105179        if (!session.HasOwnerId)
 44180            return;
 181
 61182        if (self.HasOccupantId && self.OccupantId != session.OwnerId)
 1183            _movementGroupMemberships.Remove(self.OccupantId);
 184
 61185        self.OccupantId = session.OwnerId;
 61186        self.HasOccupantId = true;
 187
 61188        _movementGroupMemberships[session.OwnerId] = new MovementGroupMembership
 61189        {
 61190            GroupId = session.GroupId,
 61191            RequestedDestination = requestedDestination,
 61192            LastSeenFrame = self.LastSeenFrame
 61193        };
 61194    }
 195
 196    private bool TryGetFormationMetrics(
 197        MovementGroupState group,
 198        Vector3d requestedDestination,
 199        int minFrame,
 200        out Vector3d groupCenter,
 201        out Fixed64 averageRadius,
 202        out int groupCount)
 203    {
 105204        groupCenter = Vector3d.Zero;
 105205        averageRadius = Fixed64.Zero;
 105206        groupCount = 0;
 207
 500208        foreach (MovementGroupMember member in group.Members)
 209        {
 145210            if (!IsEligibleGroupMember(member, requestedDestination, minFrame))
 211                continue;
 212
 133213            groupCenter += member.Position;
 133214            averageRadius += member.Radius;
 133215            groupCount++;
 216        }
 217
 105218        if (groupCount < MinMovementGroupSize)
 77219            return false;
 220
 28221        groupCenter /= groupCount;
 28222        averageRadius = (averageRadius / groupCount) + (VoxelSize * Fixed64.Half);
 28223        return true;
 224    }
 225
 226    private static Fixed64 UpdateFormationOffsets(
 227        MovementGroupState group,
 228        Vector3d requestedDestination,
 229        int minFrame,
 230        Vector3d groupCenter)
 231    {
 28232        Fixed64 maxSpreadSq = Fixed64.Zero;
 233
 168234        foreach (MovementGroupMember member in group.Members)
 235        {
 56236            if (!IsEligibleGroupMember(member, requestedDestination, minFrame))
 237                continue;
 238
 56239            Vector3d formationOffset = member.Position - groupCenter;
 56240            if (!member.HasFormationOffset)
 241            {
 34242                member.FormationOffset = formationOffset;
 34243                member.HasFormationOffset = true;
 244            }
 245
 56246            Fixed64 spreadSq = formationOffset.SqrMagnitude;
 56247            if (spreadSq > maxSpreadSq)
 20248                maxSpreadSq = spreadSq;
 249        }
 250
 28251        return maxSpreadSq;
 252    }
 253
 254    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 255    private static bool IsEligibleGroupMember(MovementGroupMember member, Vector3d requestedDestination, int minFrame)
 256    {
 201257        return member.LastSeenFrame >= minFrame
 201258            && member.RequestedDestination == requestedDestination;
 259    }
 260}

Methods/Properties

.ctor(Trailblazer.TrailblazerWorldContext)
get_FrameCount()
get_VoxelSize()
CacheOwner(Trailblazer.Navigation.MovementGroups.MovementGroupSession,System.Guid)
Prewarm(Trailblazer.Navigation.MovementGroups.MovementGroupSession,System.Guid,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
UpdateTarget(Trailblazer.Navigation.MovementGroups.MovementGroupSession,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Boolean)
Remove(Trailblazer.Navigation.MovementGroups.MovementGroupSession)
IsNeighbor(Trailblazer.Navigation.MovementGroups.MovementGroupSession,System.Guid,FixedMathSharp.Vector3d,System.Int32)
Reset()
GetOrCreateGroup(System.Int32)
GetOrCreateMember(Trailblazer.Navigation.MovementGroups.MovementGroupSession,Trailblazer.Navigation.MovementGroups.MovementGroupState,System.Boolean&)
UpdateMemberState(Trailblazer.Navigation.MovementGroups.MovementGroupMember,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,System.Boolean)
UpdateMembership(Trailblazer.Navigation.MovementGroups.MovementGroupSession,Trailblazer.Navigation.MovementGroups.MovementGroupMember,FixedMathSharp.Vector3d)
TryGetFormationMetrics(Trailblazer.Navigation.MovementGroups.MovementGroupState,FixedMathSharp.Vector3d,System.Int32,FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&,System.Int32&)
UpdateFormationOffsets(Trailblazer.Navigation.MovementGroups.MovementGroupState,FixedMathSharp.Vector3d,System.Int32,FixedMathSharp.Vector3d)
IsEligibleGroupMember(Trailblazer.Navigation.MovementGroups.MovementGroupMember,FixedMathSharp.Vector3d,System.Int32)