< Summary

Information
Class: GridForge.Grids.GridOccupantManager
Assembly: GridForge
File(s): /home/runner/work/GridForge/GridForge/src/GridForge/Grids/Managers/GridOccupantManager.cs
Line coverage
98%
Covered lines: 226
Uncovered lines: 3
Coverable lines: 229
Total lines: 614
Line coverage: 98.6%
Branch coverage
95%
Covered branches: 180
Total branches: 188
Branch coverage: 95.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/GridForge/GridForge/src/GridForge/Grids/Managers/GridOccupantManager.cs

#LineLine coverage
 1using GridForge.Spatial;
 2using SwiftCollections;
 3using SwiftCollections.Pool;
 4using System;
 5using System.Collections.Concurrent;
 6using System.Collections.Generic;
 7
 8namespace GridForge.Grids;
 9
 10/// <summary>
 11/// Provides utility methods for managing voxel occupants within a grid.
 12/// Supports adding, removing, and retrieving occupants with thread-safe operations.
 13/// </summary>
 14public static class GridOccupantManager
 15{
 16    #region Constants & Events
 17
 18    /// <summary>
 19    /// Maximum number of occupants allowed per voxel.
 20    /// </summary>
 21    public const byte MaxOccupantCount = byte.MaxValue;
 22
 23    /// <summary>
 24    /// Event triggered when an occupant is added.
 25    /// </summary>
 26    private static Action<OccupantEventInfo>? _onOccupantAdded;
 27
 28    /// <inheritdoc cref="_onOccupantAdded"/>
 29    public static event Action<OccupantEventInfo> OnOccupantAdded
 30    {
 331        add => _onOccupantAdded += value;
 332        remove => _onOccupantAdded -= value;
 33    }
 34
 35    /// <summary>
 36    /// Event triggered when an occupant is removed.
 37    /// </summary>
 38    private static Action<OccupantEventInfo>? _onOccupantRemoved;
 39
 40    /// <inheritdoc cref="_onOccupantRemoved"/>
 41    public static event Action<OccupantEventInfo> OnOccupantRemoved
 42    {
 343        add => _onOccupantRemoved += value;
 344        remove => _onOccupantRemoved -= value;
 45    }
 46
 47    #endregion
 48
 49    #region Private Fields
 50
 51    /// <summary>
 52    /// Tracks occupant registrations independently for each world.
 53    /// </summary>
 154    private static readonly ConcurrentDictionary<GridWorld, WorldOccupancyRegistry> _occupancyRegistries = new();
 55
 56    /// <summary>
 57    /// Tracks all voxel registrations for a single occupant.
 58    /// </summary>
 59    private sealed class OccupancyRecord
 60    {
 61        public readonly IVoxelOccupant Occupant;
 31462        public readonly SwiftDictionary<WorldVoxelIndex, int> Tickets = new();
 63
 31464        public OccupancyRecord(IVoxelOccupant occupant)
 65        {
 31466            Occupant = occupant;
 31467        }
 68    }
 69
 70    /// <summary>
 71    /// Synchronizes one world's tracked occupant registrations.
 72    /// </summary>
 73    private sealed class WorldOccupancyRegistry
 74    {
 3775        public readonly object SyncRoot = new();
 3776        public readonly SwiftDictionary<Guid, OccupancyRecord> Records = new();
 77    }
 78
 79    /// <summary>
 80    /// Immutable snapshot of one tracked occupancy.
 81    /// </summary>
 82    private readonly struct TrackedOccupancy
 83    {
 84        public readonly WorldVoxelIndex VoxelIndex;
 85        public readonly int Ticket;
 86
 87        public TrackedOccupancy(WorldVoxelIndex voxelIndex, int ticket)
 88        {
 16189            VoxelIndex = voxelIndex;
 16190            Ticket = ticket;
 16191        }
 92    }
 93
 94    #endregion
 95
 96    #region Occupant Management
 97
 98    /// <summary>
 99    /// Attempts to register the occupant with the current voxel it is on in the supplied world.
 100    /// </summary>
 101    public static bool TryRegister(GridWorld world, IVoxelOccupant occupant)
 102    {
 5103        return occupant != null
 5104            && world != null
 5105            && world.TryGetGridAndVoxel(occupant.Position, out VoxelGrid? grid, out Voxel? voxel)
 5106            && grid!.TryAddVoxelOccupant(voxel!, occupant);
 107    }
 108
 109    /// <summary>
 110    /// Returns a snapshot of the voxel indices currently tracked for the occupant in the supplied world.
 111    /// </summary>
 112    /// <param name="world">The world whose occupancy registry should be queried.</param>
 113    /// <param name="occupant">The occupant whose tracked voxel indices should be returned.</param>
 114    /// <returns>A deterministic snapshot of the occupant's currently tracked voxel indices.</returns>
 115    public static IEnumerable<WorldVoxelIndex> GetOccupiedIndices(GridWorld world, IVoxelOccupant occupant)
 116    {
 10117        TrackedOccupancy[] occupancies = GetTrackedOccupanciesSnapshot(world, occupant);
 10118        if (occupancies.Length == 0)
 6119            return Array.Empty<WorldVoxelIndex>();
 120
 4121        WorldVoxelIndex[] indices = new WorldVoxelIndex[occupancies.Length];
 20122        for (int i = 0; i < occupancies.Length; i++)
 6123            indices[i] = occupancies[i].VoxelIndex;
 124
 4125        return indices;
 126    }
 127
 128    /// <summary>
 129    /// Attempts to retrieve the scan-cell ticket for the occupant at a specific voxel in the supplied world.
 130    /// </summary>
 131    /// <param name="world">The world whose occupancy registry should be queried.</param>
 132    /// <param name="occupant">The occupant whose registration should be queried.</param>
 133    /// <param name="index">The tracked voxel index to look up.</param>
 134    /// <param name="ticket">The tracked scan-cell ticket for the occupant at that voxel.</param>
 135    /// <returns>True if the occupant is registered to the voxel, otherwise false.</returns>
 136    public static bool TryGetOccupancyTicket(
 137        GridWorld world,
 138        IVoxelOccupant occupant,
 139        WorldVoxelIndex index,
 140        out int ticket)
 141    {
 163142        ticket = -1;
 163143        if (world == null || occupant == null || !TryGetWorldRegistry(world, out WorldOccupancyRegistry? registry))
 3144            return false;
 145
 160146        lock (registry!.SyncRoot)
 160147            return TryGetTrackedRecordUnsafe(world, occupant, out OccupancyRecord? record)
 160148                && record!.Tickets.TryGetValue(index, out ticket);
 160149    }
 150
 151    /// <summary>
 152    /// Attempts to add an occupant from the given world-scoped voxel identity in the supplied world.
 153    /// </summary>
 154    public static bool TryAddVoxelOccupant(
 155        GridWorld world,
 156        WorldVoxelIndex index,
 157        IVoxelOccupant occupant)
 158    {
 2159        return occupant != null
 2160            && world != null
 2161            && world.TryGetGridAndVoxel(index, out VoxelGrid? grid, out Voxel? voxel)
 2162            && grid!.TryAddVoxelOccupant(voxel!, occupant);
 163    }
 164
 165    /// <summary>
 166    /// Attempts to add an occupant at the given world position.
 167    /// </summary>
 168    public static bool TryAddVoxelOccupant(this VoxelGrid grid, IVoxelOccupant occupant)
 169    {
 300170        return occupant != null
 300171            && grid.TryGetVoxel(occupant.Position, out Voxel? voxel)
 300172            && grid.TryAddVoxelOccupant(voxel!, occupant);
 173    }
 174
 175    /// <summary>
 176    /// Attempts to add an occupant at the specified voxel index.
 177    /// </summary>
 178    public static bool TryAddVoxelOccupant(
 179        this VoxelGrid grid,
 180        VoxelIndex voxelIndex,
 181        IVoxelOccupant occupant)
 182    {
 2183        return occupant != null
 2184            && grid.TryGetVoxel(voxelIndex, out Voxel? target)
 2185            && grid.TryAddVoxelOccupant(target!, occupant);
 186    }
 187
 188    /// <summary>
 189    /// Adds an occupant to the grid.
 190    /// </summary>
 191    public static bool TryAddVoxelOccupant(
 192        this VoxelGrid grid,
 193        Voxel targetVoxel,
 194        IVoxelOccupant occupant)
 195    {
 318196        if (occupant == null || grid.World == null)
 1197            return false;
 198
 317199        if (!targetVoxel.HasVacancy || !grid.TryGetScanCell(targetVoxel.ScanCellKey, out ScanCell? scanCell))
 1200            return false;
 201
 202        int ticket;
 203        byte occupantCount;
 204
 316205        lock (grid.OccupantSyncRoot)
 206        {
 316207            ticket = scanCell!.AddOccupant(targetVoxel.WorldIndex, occupant);
 316208            if (!TryTrackOccupancy(grid.World, occupant, targetVoxel.WorldIndex, ticket))
 209            {
 2210                scanCell.TryRemoveOccupant(targetVoxel.WorldIndex, ticket);
 2211                return false;
 212            }
 213
 314214            grid.ActiveScanCells ??= SwiftHashSetPool<int>.Shared.Rent();
 314215            if (!grid.ActiveScanCells.Contains(targetVoxel.ScanCellKey))
 42216                grid.ActiveScanCells.Add(targetVoxel.ScanCellKey);
 217
 314218            targetVoxel.OccupantCount++;
 314219            occupantCount = targetVoxel.OccupantCount;
 314220        }
 221
 314222        NotifyOccupantAdded(targetVoxel, occupant, ticket, occupantCount);
 223
 314224        return true;
 2225    }
 226
 227    /// <summary>
 228    /// Attempts to de-register the occupant from every voxel currently tracked in the supplied world.
 229    /// </summary>
 230    public static bool TryDeregister(GridWorld world, IVoxelOccupant occupant)
 231    {
 4232        TrackedOccupancy[] occupancies = GetTrackedOccupanciesSnapshot(world, occupant);
 4233        if (occupancies.Length == 0)
 1234            return false;
 235
 3236        bool removedAny = false;
 237
 14238        for (int i = 0; i < occupancies.Length; i++)
 239        {
 4240            WorldVoxelIndex voxelIndex = occupancies[i].VoxelIndex;
 4241            if (!world.TryGetGridAndVoxel(voxelIndex, out VoxelGrid? grid, out Voxel? voxel))
 242            {
 1243                removedAny |= ForgetTrackedOccupancy(world, occupant, voxelIndex);
 1244                continue;
 245            }
 246
 3247            removedAny |= grid!.TryRemoveVoxelOccupant(voxel!, occupant);
 248        }
 249
 3250        return removedAny;
 251    }
 252
 253    /// <summary>
 254    /// Attempts to remove an occupant from the given world-scoped voxel identity in the supplied world.
 255    /// </summary>
 256    public static bool TryRemoveVoxelOccupant(
 257        GridWorld world,
 258        WorldVoxelIndex index,
 259        IVoxelOccupant occupant)
 260    {
 2261        return occupant != null
 2262            && world != null
 2263            && world.TryGetGridAndVoxel(index, out VoxelGrid? grid, out Voxel? voxel)
 2264            && grid!.TryRemoveVoxelOccupant(voxel!, occupant);
 265    }
 266
 267    /// <summary>
 268    /// Attempts to remove an occupant from the given world position.
 269    /// </summary>
 270    public static bool TryRemoveVoxelOccupant(
 271        this VoxelGrid grid,
 272        IVoxelOccupant occupant)
 273    {
 138274        if (grid.World == null)
 0275            return false;
 276
 138277        TrackedOccupancy[] occupancies = GetTrackedOccupanciesSnapshot(grid.World, occupant, grid.GridIndex);
 138278        if (occupancies.Length == 0)
 3279            return false;
 280
 135281        bool removedAny = false;
 282
 540283        for (int i = 0; i < occupancies.Length; i++)
 284        {
 135285            WorldVoxelIndex voxelIndex = occupancies[i].VoxelIndex;
 135286            if (voxelIndex.WorldSpawnToken != grid.World.SpawnToken
 135287                || voxelIndex.GridSpawnToken != grid.SpawnToken
 135288                || !grid.TryGetVoxel(voxelIndex.VoxelIndex, out Voxel? voxel))
 289            {
 2290                removedAny |= ForgetTrackedOccupancy(grid.World, occupant, voxelIndex);
 2291                continue;
 292            }
 293
 133294            removedAny |= TryRemoveVoxelOccupant(grid, voxel!, occupant);
 295        }
 296
 135297        return removedAny;
 298    }
 299
 300    /// <summary>
 301    /// Attempts to remove an occupant at the specified voxel coordinates.
 302    /// </summary>
 303    public static bool TryRemoveVoxelOccupant(
 304        this VoxelGrid grid,
 305        VoxelIndex voxelIndex,
 306        IVoxelOccupant occupant)
 307    {
 4308        return occupant != null
 4309            && grid.TryGetVoxel(voxelIndex, out Voxel? targetVoxel)
 4310            && grid.TryRemoveVoxelOccupant(targetVoxel!, occupant);
 311    }
 312
 313    /// <summary>
 314    /// Removes an occupant from this grid.
 315    /// </summary>
 316    public static bool TryRemoveVoxelOccupant(
 317        this VoxelGrid grid,
 318        Voxel targetVoxel,
 319        IVoxelOccupant occupant)
 320    {
 150321        if (occupant == null || grid.World == null)
 1322            return false;
 323
 149324        if (!TryGetOccupancyTicket(grid.World, occupant, targetVoxel.WorldIndex, out int ticket))
 325        {
 2326            GridForgeLogger.Channel.Warn($"Occupant {occupant.GlobalId} is not registered to voxel {targetVoxel.WorldInd
 2327            return false;
 328        }
 329
 147330        if (!targetVoxel.IsOccupied || grid.TryGetScanCell(targetVoxel.ScanCellKey, out ScanCell? scanCell) != true)
 1331            return false;
 332
 146333        bool success = false;
 146334        byte occupantCount = targetVoxel.OccupantCount;
 335
 146336        lock (grid.OccupantSyncRoot)
 337        {
 146338            success = scanCell!.TryRemoveOccupant(targetVoxel.WorldIndex, ticket);
 146339            if (success)
 340            {
 146341                ForgetTrackedOccupancy(grid.World, occupant, targetVoxel.WorldIndex);
 342
 146343                if (!scanCell.IsOccupied && grid.ActiveScanCells != null)
 344                {
 14345                    grid.ActiveScanCells.Remove(targetVoxel.ScanCellKey);
 14346                    if (!grid.IsOccupied)
 347                    {
 14348                        GridForgeLogger.Channel.Info($"Releasing unused active scan cells collection.");
 14349                        SwiftHashSetPool<int>.Shared.Release(grid.ActiveScanCells);
 14350                        grid.ActiveScanCells = null;
 351                    }
 352                }
 353
 146354                targetVoxel.OccupantCount--;
 146355                occupantCount = targetVoxel.OccupantCount;
 356            }
 146357        }
 358
 146359        if (success)
 146360            NotifyOccupantRemoved(targetVoxel, occupant, ticket, occupantCount);
 361
 146362        return success;
 363    }
 364
 365    #endregion
 366
 367    #region Internal Helpers
 368
 369    internal static void ForgetTrackedOccupancies(
 370        GridWorld? world,
 371        IEnumerable<IVoxelOccupant> occupants,
 372        WorldVoxelIndex index)
 373    {
 98374        if (world == null)
 0375            return;
 376
 532377        foreach (IVoxelOccupant occupant in occupants)
 168378            ForgetTrackedOccupancy(world, occupant, index);
 98379    }
 380
 381    internal static void ClearTrackedOccupancies(GridWorld world)
 382    {
 179383        if (world == null || !TryGetWorldRegistry(world, out WorldOccupancyRegistry? registry))
 142384            return;
 385
 37386        lock (registry!.SyncRoot)
 37387            registry.Records.Clear();
 37388    }
 389
 390    internal static void ReleaseTrackedOccupancies(GridWorld world)
 391    {
 178392        if (world == null || !_occupancyRegistries.TryRemove(world, out WorldOccupancyRegistry? registry))
 141393            return;
 394
 37395        lock (registry.SyncRoot)
 37396            registry.Records.Clear();
 37397    }
 398
 399    #endregion
 400
 401    #region Private Methods
 402
 403    private static bool TryTrackOccupancy(
 404        GridWorld world,
 405        IVoxelOccupant occupant,
 406        WorldVoxelIndex index,
 407        int ticket)
 408    {
 320409        if (world == null)
 0410            return false;
 411
 320412        WorldOccupancyRegistry registry = GetWorldRegistry(world);
 320413        lock (registry.SyncRoot)
 414        {
 320415            if (registry.Records.TryGetValue(occupant.GlobalId, out OccupancyRecord record))
 416            {
 6417                if (!ReferenceEquals(record.Occupant, occupant))
 418                {
 1419                    GridForgeLogger.Channel.Warn($"Occupant id collision detected for {occupant.GlobalId} in world {worl
 1420                    return false;
 421                }
 422            }
 423            else
 424            {
 314425                record = new OccupancyRecord(occupant);
 314426                registry.Records[occupant.GlobalId] = record;
 427            }
 428
 319429            if (record.Tickets.ContainsKey(index))
 430            {
 1431                GridForgeLogger.Channel.Warn($"Occupant {occupant.GlobalId} is already registered to voxel {index}.");
 1432                return false;
 433            }
 434
 318435            record.Tickets[index] = ticket;
 318436            return true;
 437        }
 320438    }
 439
 440    private static bool ForgetTrackedOccupancy(
 441        GridWorld world,
 442        IVoxelOccupant occupant,
 443        WorldVoxelIndex index)
 444    {
 320445        if (world == null || occupant == null || !TryGetWorldRegistry(world, out WorldOccupancyRegistry? registry))
 1446            return false;
 447
 319448        lock (registry!.SyncRoot)
 449        {
 319450            if (!TryGetTrackedRecordUnsafe(world, occupant, out OccupancyRecord? record) || record!.Tickets.ContainsKey(
 1451                return false;
 452
 318453            record.Tickets.Remove(index);
 318454            if (record.Tickets.Count == 0)
 314455                registry.Records.Remove(occupant.GlobalId);
 456
 318457            return true;
 458        }
 319459    }
 460
 461    private static bool TryGetTrackedRecordUnsafe(
 462        GridWorld world,
 463        IVoxelOccupant occupant,
 464        out OccupancyRecord? record)
 465    {
 630466        record = null;
 630467        if (world == null
 630468            || occupant == null
 630469            || !TryGetWorldRegistry(world, out WorldOccupancyRegistry? registry)
 630470            || !registry!.Records.TryGetValue(occupant.GlobalId, out record))
 471        {
 9472            return false;
 473        }
 474
 621475        return ReferenceEquals(record.Occupant, occupant);
 476    }
 477
 478    private static TrackedOccupancy[] GetTrackedOccupanciesSnapshot(
 479        GridWorld world,
 480        IVoxelOccupant occupant,
 481        ushort? gridIndex = null)
 482    {
 152483        if (world == null || occupant == null || !TryGetWorldRegistry(world, out WorldOccupancyRegistry? registry))
 2484            return Array.Empty<TrackedOccupancy>();
 485
 150486        lock (registry!.SyncRoot)
 487        {
 150488            if (!TryGetTrackedRecordUnsafe(world, occupant, out OccupancyRecord? record) || record!.Tickets.Count == 0)
 7489                return Array.Empty<TrackedOccupancy>();
 490
 143491            TrackedOccupancy[] occupancies = new TrackedOccupancy[record.Tickets.Count];
 143492            int count = 0;
 493
 582494            foreach (WorldVoxelIndex index in record.Tickets.Keys)
 495            {
 148496                if (gridIndex.HasValue && index.GridIndex != gridIndex.Value)
 497                    continue;
 498
 145499                occupancies[count++] = new TrackedOccupancy(index, record.Tickets[index]);
 500            }
 501
 143502            if (count == 0)
 1503                return Array.Empty<TrackedOccupancy>();
 504
 142505            if (count != occupancies.Length)
 1506                Array.Resize(ref occupancies, count);
 507
 142508            Array.Sort(occupancies, CompareTrackedOccupancies);
 142509            return occupancies;
 510        }
 150511    }
 512
 513    private static int CompareTrackedOccupancies(TrackedOccupancy left, TrackedOccupancy right)
 514    {
 12515        int result = left.VoxelIndex.WorldSpawnToken.CompareTo(right.VoxelIndex.WorldSpawnToken);
 12516        if (result != 0)
 2517            return result;
 518
 10519        result = left.VoxelIndex.GridIndex.CompareTo(right.VoxelIndex.GridIndex);
 10520        if (result != 0)
 3521            return result;
 522
 7523        result = left.VoxelIndex.VoxelIndex.x.CompareTo(right.VoxelIndex.VoxelIndex.x);
 7524        if (result != 0)
 2525            return result;
 526
 5527        result = left.VoxelIndex.VoxelIndex.y.CompareTo(right.VoxelIndex.VoxelIndex.y);
 5528        if (result != 0)
 1529            return result;
 530
 4531        result = left.VoxelIndex.VoxelIndex.z.CompareTo(right.VoxelIndex.VoxelIndex.z);
 4532        if (result != 0)
 1533            return result;
 534
 3535        result = left.VoxelIndex.GridSpawnToken.CompareTo(right.VoxelIndex.GridSpawnToken);
 3536        if (result != 0)
 1537            return result;
 538
 2539        return left.Ticket.CompareTo(right.Ticket);
 540    }
 541
 542    /// <summary>
 543    /// Notifies listeners that an occupant was added.
 544    /// </summary>
 545    private static void NotifyOccupantAdded(
 546        Voxel targetVoxel,
 547        IVoxelOccupant occupant,
 548        int ticket,
 549        byte occupantCount)
 550    {
 314551        OccupantEventInfo eventInfo = new(targetVoxel.WorldIndex, occupant, ticket, occupantCount);
 314552        Action<OccupantEventInfo>? handlers = _onOccupantAdded;
 314553        if (handlers != null)
 554        {
 2555            var handlerDelegates = handlers.GetInvocationList();
 10556            for (int i = 0; i < handlerDelegates.Length; i++)
 557            {
 558                try
 559                {
 3560                    ((Action<OccupantEventInfo>)handlerDelegates[i])(eventInfo);
 2561                }
 1562                catch (Exception ex)
 563                {
 1564                    GridForgeLogger.Channel.Error($"[Voxel {targetVoxel.WorldIndex}] Occupant add error: {ex.Message}");
 1565                }
 566            }
 567        }
 568
 314569        targetVoxel.NotifyOccupantAdded(eventInfo);
 314570    }
 571
 572    /// <summary>
 573    /// Notifies listeners that an occupant was removed.
 574    /// </summary>
 575    private static void NotifyOccupantRemoved(
 576        Voxel targetVoxel,
 577        IVoxelOccupant occupant,
 578        int ticket,
 579        byte occupantCount)
 580    {
 146581        OccupantEventInfo eventInfo = new(targetVoxel.WorldIndex, occupant, ticket, occupantCount);
 146582        Action<OccupantEventInfo>? handlers = _onOccupantRemoved;
 146583        if (handlers != null)
 584        {
 2585            var handlerDelegates = handlers.GetInvocationList();
 10586            for (int i = 0; i < handlerDelegates.Length; i++)
 587            {
 588                try
 589                {
 3590                    ((Action<OccupantEventInfo>)handlerDelegates[i])(eventInfo);
 2591                }
 1592                catch (Exception ex)
 593                {
 1594                    GridForgeLogger.Channel.Error($"[Voxel {targetVoxel.WorldIndex}] Occupant remove error: {ex.Message}
 1595                }
 596            }
 597        }
 598
 146599        targetVoxel.NotifyOccupantRemoved(eventInfo);
 146600    }
 601
 602    private static WorldOccupancyRegistry GetWorldRegistry(GridWorld world)
 603    {
 357604        return _occupancyRegistries.GetOrAdd(world, static _ => new WorldOccupancyRegistry());
 605    }
 606
 607    private static bool TryGetWorldRegistry(GridWorld world, out WorldOccupancyRegistry? registry)
 608    {
 1440609        registry = null;
 1440610        return world != null && _occupancyRegistries.TryGetValue(world, out registry);
 611    }
 612
 613    #endregion
 614}

Methods/Properties

add_OnOccupantAdded(System.Action`1<GridForge.Grids.OccupantEventInfo>)
remove_OnOccupantAdded(System.Action`1<GridForge.Grids.OccupantEventInfo>)
add_OnOccupantRemoved(System.Action`1<GridForge.Grids.OccupantEventInfo>)
remove_OnOccupantRemoved(System.Action`1<GridForge.Grids.OccupantEventInfo>)
.cctor()
.ctor(GridForge.Spatial.IVoxelOccupant)
.ctor()
.ctor(GridForge.Spatial.WorldVoxelIndex,System.Int32)
TryRegister(GridForge.Grids.GridWorld,GridForge.Spatial.IVoxelOccupant)
GetOccupiedIndices(GridForge.Grids.GridWorld,GridForge.Spatial.IVoxelOccupant)
TryGetOccupancyTicket(GridForge.Grids.GridWorld,GridForge.Spatial.IVoxelOccupant,GridForge.Spatial.WorldVoxelIndex,System.Int32&)
TryAddVoxelOccupant(GridForge.Grids.GridWorld,GridForge.Spatial.WorldVoxelIndex,GridForge.Spatial.IVoxelOccupant)
TryAddVoxelOccupant(GridForge.Grids.VoxelGrid,GridForge.Spatial.IVoxelOccupant)
TryAddVoxelOccupant(GridForge.Grids.VoxelGrid,GridForge.Spatial.VoxelIndex,GridForge.Spatial.IVoxelOccupant)
TryAddVoxelOccupant(GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel,GridForge.Spatial.IVoxelOccupant)
TryDeregister(GridForge.Grids.GridWorld,GridForge.Spatial.IVoxelOccupant)
TryRemoveVoxelOccupant(GridForge.Grids.GridWorld,GridForge.Spatial.WorldVoxelIndex,GridForge.Spatial.IVoxelOccupant)
TryRemoveVoxelOccupant(GridForge.Grids.VoxelGrid,GridForge.Spatial.IVoxelOccupant)
TryRemoveVoxelOccupant(GridForge.Grids.VoxelGrid,GridForge.Spatial.VoxelIndex,GridForge.Spatial.IVoxelOccupant)
TryRemoveVoxelOccupant(GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel,GridForge.Spatial.IVoxelOccupant)
ForgetTrackedOccupancies(GridForge.Grids.GridWorld,System.Collections.Generic.IEnumerable`1<GridForge.Spatial.IVoxelOccupant>,GridForge.Spatial.WorldVoxelIndex)
ClearTrackedOccupancies(GridForge.Grids.GridWorld)
ReleaseTrackedOccupancies(GridForge.Grids.GridWorld)
TryTrackOccupancy(GridForge.Grids.GridWorld,GridForge.Spatial.IVoxelOccupant,GridForge.Spatial.WorldVoxelIndex,System.Int32)
ForgetTrackedOccupancy(GridForge.Grids.GridWorld,GridForge.Spatial.IVoxelOccupant,GridForge.Spatial.WorldVoxelIndex)
TryGetTrackedRecordUnsafe(GridForge.Grids.GridWorld,GridForge.Spatial.IVoxelOccupant,GridForge.Grids.GridOccupantManager/OccupancyRecord&)
GetTrackedOccupanciesSnapshot(GridForge.Grids.GridWorld,GridForge.Spatial.IVoxelOccupant,System.Nullable`1<System.UInt16>)
CompareTrackedOccupancies(GridForge.Grids.GridOccupantManager/TrackedOccupancy,GridForge.Grids.GridOccupantManager/TrackedOccupancy)
NotifyOccupantAdded(GridForge.Grids.Voxel,GridForge.Spatial.IVoxelOccupant,System.Int32,System.Byte)
NotifyOccupantRemoved(GridForge.Grids.Voxel,GridForge.Spatial.IVoxelOccupant,System.Int32,System.Byte)
GetWorldRegistry(GridForge.Grids.GridWorld)
TryGetWorldRegistry(GridForge.Grids.GridWorld,GridForge.Grids.GridOccupantManager/WorldOccupancyRegistry&)