private ConveyorEndpointMapping ComputeMappingForBlock(IMyConveyorEndpointBlock processedBlock)
        {
            ConveyorEndpointMapping endpointMap = new ConveyorEndpointMapping();

            // Process pull mapping
            PullInformation pullInformation = processedBlock.GetPullInformation();
            if (pullInformation != null)
            {
                endpointMap.pullElements = new List<IMyConveyorEndpointBlock>();

                lock (Pathfinding)
                {
                    SetTraversalPlayerId(pullInformation.OwnerID);

                    // Pulling one specific item?
                    if (pullInformation.ItemDefinition != default(MyDefinitionId))
                    {
                        SetTraversalInventoryItemDefinitionId(pullInformation.ItemDefinition);

                        using (var invertedConductivity = new MyConveyorLine.InvertedConductivity())
                        {
                            PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate, NeedsLargeTube(pullInformation.ItemDefinition) ? IsConveyorLargePredicate : null);
                            foreach (var conveyorEndpoint in Pathfinding)
                            {
                                // Ignore endpoints without a block
                                if (conveyorEndpoint.CubeBlock == null) continue;

                                // Ignore blocks without inventory
                                if (!conveyorEndpoint.CubeBlock.HasInventory) continue;

                                // Ignore blocks that do not implement IMyConveyorEndpointBlock interface
                                IMyConveyorEndpointBlock endpointBlock = conveyorEndpoint.CubeBlock as IMyConveyorEndpointBlock;
                                if (endpointBlock == null) continue;

                                // Iterate inventories to make sure we can take the items
                                bool isInventoryAvailable = false;
                                for (int i = 0; i < conveyorEndpoint.CubeBlock.InventoryCount; ++i)
                                {
                                    var inventory = conveyorEndpoint.CubeBlock.GetInventory(i) as MyInventory;
                                    System.Diagnostics.Debug.Assert(inventory != null, "Null or other inventory type!");

                                    if ((inventory.GetFlags() & MyInventoryFlags.CanSend) == 0)
                                        continue;

                                    isInventoryAvailable = true;
                                    break;
                                }

                                if (isInventoryAvailable)
                                    endpointMap.pullElements.Add(endpointBlock);
                            }
                        }
                    }

                    else if (pullInformation.Constraint != null)
                    {
                        SetTraversalInventoryItemDefinitionId();
                        using (var invertedConductivity = new MyConveyorLine.InvertedConductivity())
                        {
                            // Once for small tubes
                            PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate, IsConveyorSmallPredicate);
                            foreach (var conveyorEndpoint in Pathfinding)
                            {
                                // Ignore originating block
                                if (conveyorEndpoint.CubeBlock == processedBlock as MyCubeBlock) continue;

                                // Ignore endpoints without a block
                                if (conveyorEndpoint.CubeBlock == null) continue;

                                // Ignore blocks without inventory
                                if (!conveyorEndpoint.CubeBlock.HasInventory) continue;

                                // Ignore blocks that do not implement IMyConveyorEndpointBlock interface
                                IMyConveyorEndpointBlock endpointBlock = conveyorEndpoint.CubeBlock as IMyConveyorEndpointBlock;
                                if (endpointBlock == null) continue;

                                // Iterate inventories to make sure we can take the items
                                bool isInventoryAvailable = false;
                                for (int i = 0; i < conveyorEndpoint.CubeBlock.InventoryCount; ++i)
                                {
                                    var inventory = conveyorEndpoint.CubeBlock.GetInventory(i) as MyInventory;
                                    System.Diagnostics.Debug.Assert(inventory != null, "Null or other inventory type!");

                                    if ((inventory.GetFlags() & MyInventoryFlags.CanSend) == 0)
                                        continue;

                                    isInventoryAvailable = true;
                                    break;
                                }

                                if (isInventoryAvailable)
                                    endpointMap.pullElements.Add(endpointBlock);
                            }

                            // Once for large tubes
                            PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate, null);
                            foreach (var conveyorEndpoint in Pathfinding)
                            {
                                // Ignore originating block
                                if (conveyorEndpoint.CubeBlock == processedBlock as MyCubeBlock) continue;

                                // Ignore endpoints without a block
                                if (conveyorEndpoint.CubeBlock == null) continue;

                                // Ignore blocks without inventory
                                if (!conveyorEndpoint.CubeBlock.HasInventory) continue;

                                // Ignore blocks that do not implement IMyConveyorEndpointBlock interface
                                IMyConveyorEndpointBlock endpointBlock = conveyorEndpoint.CubeBlock as IMyConveyorEndpointBlock;
                                if (endpointBlock == null) continue;

                                // Iterate inventories to make sure we can take the items
                                bool isInventoryAvailable = false;
                                for (int i = 0; i < conveyorEndpoint.CubeBlock.InventoryCount; ++i)
                                {
                                    var inventory = conveyorEndpoint.CubeBlock.GetInventory(i) as MyInventory;
                                    System.Diagnostics.Debug.Assert(inventory != null, "Null or other inventory type!");

                                    if ((inventory.GetFlags() & MyInventoryFlags.CanSend) == 0)
                                        continue;

                                    isInventoryAvailable = true;
                                    break;
                                }

                                if (isInventoryAvailable)
                                {
                                    if (!endpointMap.pullElements.Contains(endpointBlock))
                                        endpointMap.pullElements.Add(endpointBlock);
                                }
                            }
                        }
                    }
                }
            }

            // Process push mapping
            PullInformation pushInformation = processedBlock.GetPushInformation();
            if (pushInformation != null)
            {
                endpointMap.pushElements = new List<IMyConveyorEndpointBlock>();

                lock (Pathfinding)
                {
                    SetTraversalPlayerId(pushInformation.OwnerID);

                    HashSet<MyDefinitionId> definitions = new HashSet<MyDefinitionId>();
                    if (pushInformation.ItemDefinition != default(MyDefinitionId))
                    {
                        definitions.Add(pushInformation.ItemDefinition);
                    }

                    if (pushInformation.Constraint != null)
                    {
                        foreach (MyDefinitionId definition in pushInformation.Constraint.ConstrainedIds)
                            definitions.Add(definition);

                        foreach (MyObjectBuilderType constrainedType in pushInformation.Constraint.ConstrainedTypes)
                        {
                            MyDefinitionManager.Static.TryGetDefinitionsByTypeId(constrainedType, definitions);
                        }
                    }

                    // Empty constraint, no need to check, anything that can take items is okay, push requests will re-test anyway
                    if (definitions.Count == 0 && (pushInformation.Constraint == null || pushInformation.Constraint.Description == "Empty constraint"))
                    {
                        SetTraversalInventoryItemDefinitionId();
                        PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate);

                        foreach (var conveyorEndpoint in MyGridConveyorSystem.Pathfinding)
                        {
                            // Ignore originating block
                            if (conveyorEndpoint.CubeBlock == processedBlock as MyCubeBlock) continue;

                            // Ignore endpoints without a block
                            if (conveyorEndpoint.CubeBlock == null) continue;

                            // Ignore blocks without inventory
                            if (!conveyorEndpoint.CubeBlock.HasInventory) continue;

                            // Ignore blocks that do not implement IMyConveyorEndpointBlock interface
                            IMyConveyorEndpointBlock endpointBlock = conveyorEndpoint.CubeBlock as IMyConveyorEndpointBlock;
                            if (endpointBlock == null) continue;

                            MyCubeBlock owner = conveyorEndpoint.CubeBlock;

                            // Iterate inventories to make sure they can take the items
                            bool isInventoryAvailable = false;
                            for (int i = 0; i < owner.InventoryCount; ++i)
                            {
                                var inventory = owner.GetInventory(i) as MyInventory;
                                System.Diagnostics.Debug.Assert(inventory != null, "Null or other inventory type!");

                                if ((inventory.GetFlags() & MyInventoryFlags.CanReceive) == 0)
                                    continue;

                                isInventoryAvailable = true;
                                break;
                            }

                            if (isInventoryAvailable && !endpointMap.pushElements.Contains(endpointBlock))
                                endpointMap.pushElements.Add(endpointBlock);
                        }
                    }
                    else
                    {
                        // Iterate through all the constrained item definitions
                        foreach (MyDefinitionId definitionId in definitions)
                        {
                            SetTraversalInventoryItemDefinitionId(definitionId);

                            if (NeedsLargeTube(definitionId))
                            {
                                PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate, IsConveyorLargePredicate);
                            }
                            else
                            {
                                PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate);
                            }

                            foreach (var conveyorEndpoint in MyGridConveyorSystem.Pathfinding)
                            {
                                // Ignore originating block
                                if (conveyorEndpoint.CubeBlock == processedBlock as MyCubeBlock) continue;

                                // Ignore endpoints without a block
                                if (conveyorEndpoint.CubeBlock == null) continue;

                                // Ignore blocks without inventory
                                if (!conveyorEndpoint.CubeBlock.HasInventory) continue;

                                // Ignore blocks that do not implement IMyConveyorEndpointBlock interface
                                IMyConveyorEndpointBlock endpointBlock = conveyorEndpoint.CubeBlock as IMyConveyorEndpointBlock;
                                if (endpointBlock == null) continue;

                                MyCubeBlock owner = conveyorEndpoint.CubeBlock;

                                // Iterate inventories to make sure they can take the items
                                bool isInventoryAvailable = false;
                                for (int i = 0; i < owner.InventoryCount; ++i)
                                {
                                    var inventory = owner.GetInventory(i) as MyInventory;
                                    System.Diagnostics.Debug.Assert(inventory != null, "Null or other inventory type!");

                                    if ((inventory.GetFlags() & MyInventoryFlags.CanReceive) == 0)
                                        continue;

                                    // Make sure target inventory can take this item
                                    if (!inventory.CheckConstraint(definitionId))
                                        continue;

                                    isInventoryAvailable = true;
                                    break;
                                }

                                if (isInventoryAvailable && !endpointMap.pushElements.Contains(endpointBlock))
                                    endpointMap.pushElements.Add(endpointBlock);
                            }
                        }
                    }
                }
            }

            return endpointMap;
        }
        private ConveyorEndpointMapping ComputeMappingForBlock(IMyConveyorEndpointBlock processedBlock)
        {
            ConveyorEndpointMapping endpointMap = new ConveyorEndpointMapping();

            // Process pull mapping
            PullInformation pullInformation = processedBlock.GetPullInformation();
            if (pullInformation != null)
            {
                endpointMap.pullElements = new List<IMyConveyorEndpointBlock>();

                lock (Pathfinding)
                {
                    SetTraversalPlayerId(pullInformation.OwnerID);

                    // Pulling one specific item?
                    if (pullInformation.ItemDefinition != default(MyDefinitionId))
                    {
                        SetTraversalInventoryItemDefinitionId(pullInformation.ItemDefinition);

                        using (var invertedConductivity = new MyConveyorLine.InvertedConductivity())
                        {
                            PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate, NeedsLargeTube(pullInformation.ItemDefinition) ? IsConveyorLargePredicate : null);
                            AddReachableEndpoints(processedBlock, endpointMap.pullElements, MyInventoryFlags.CanSend);
                        }
                    }

                    else if (pullInformation.Constraint != null)
                    {
                        SetTraversalInventoryItemDefinitionId();
                        using (var invertedConductivity = new MyConveyorLine.InvertedConductivity())
                        {
                            // Once for small tubes
                            PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate, IsConveyorSmallPredicate);
                            AddReachableEndpoints(processedBlock, endpointMap.pullElements, MyInventoryFlags.CanSend);

                            // Once for large tubes
                            PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate, null);
                            AddReachableEndpoints(processedBlock, endpointMap.pullElements, MyInventoryFlags.CanSend);
                        }
                    }
                }
            }

            // Process push mapping
            PullInformation pushInformation = processedBlock.GetPushInformation();
            if (pushInformation != null)
            {
                endpointMap.pushElements = new List<IMyConveyorEndpointBlock>();

                lock (Pathfinding)
                {
                    SetTraversalPlayerId(pushInformation.OwnerID);

                    HashSet<MyDefinitionId> definitions = new HashSet<MyDefinitionId>();
                    if (pushInformation.ItemDefinition != default(MyDefinitionId))
                    {
                        definitions.Add(pushInformation.ItemDefinition);
                    }

                    if (pushInformation.Constraint != null)
                    {
                        foreach (MyDefinitionId definition in pushInformation.Constraint.ConstrainedIds)
                            definitions.Add(definition);

                        foreach (MyObjectBuilderType constrainedType in pushInformation.Constraint.ConstrainedTypes)
                        {
                            MyDefinitionManager.Static.TryGetDefinitionsByTypeId(constrainedType, definitions);
                        }
                    }

                    // Empty constraint, no need to check, anything that can take items is okay, push requests will re-test anyway
                    if (definitions.Count == 0 && (pushInformation.Constraint == null || pushInformation.Constraint.Description == "Empty constraint"))
                    {
                        SetTraversalInventoryItemDefinitionId();
                        PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate);
                        AddReachableEndpoints(processedBlock, endpointMap.pushElements, MyInventoryFlags.CanReceive);
                    }
                    else
                    {
                        // Iterate through all the constrained item definitions
                        foreach (MyDefinitionId definitionId in definitions)
                        {
                            SetTraversalInventoryItemDefinitionId(definitionId);

                            if (NeedsLargeTube(definitionId))
                                PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate, IsConveyorLargePredicate);
                            else
                                PrepareTraversal(processedBlock.ConveyorEndpoint, null, IsAccessAllowedPredicate);

                            AddReachableEndpoints(processedBlock, endpointMap.pushElements, MyInventoryFlags.CanReceive, definitionId);
                        }
                    }
                }
            }

            return endpointMap;
        }