protected override IEnumerator <bool> ProcessVoxels(HashSet <IMyVoxelBase> Voxels) { if (!MyKernel.Session.Settings.EnableDrilling) { yield return(false); } if (Voxels.Count == 0) { yield return(false); } if (MyKernel.TermControls.ToolMode == true) { yield return(false); } if (!SafeZonesHelper.IsActionAllowed(MyKernel.Block.GetPosition(), MySafeZoneAction.Drilling, MyKernel.Block.EntityId)) { yield return(false); } //WriteToLog("ProcessVoxels", $"Processing {Voxels.Count} voxels"); Stopwatch stopwatch = Stopwatch.StartNew(); LineD WorkingRay = new LineD(MyKernel.BeamDrawer.BeamStart, MyKernel.BeamDrawer.BeamEnd); foreach (MyVoxelBase Voxel in Voxels.OfType <MyVoxelBase>()) { if (!SafeZonesHelper.IsActionAllowed((Voxel as IMyEntity).WorldAABB, MySafeZoneAction.Drilling, MyKernel.Block.EntityId)) { continue; } if (Voxel.GetOrePriority() == -1) { continue; // Idk why, but the same early exit is found in MyShipDrill } Vector3D?VoxelHit = Voxel.GetClosestPointOnRay(WorkingRay, step: 0.99f); if (VoxelHit.HasValue) { MyKernel.ResponderModule.UpdateStatusReport($"\r\nFound a voxel", true); lock (BlockedVoxels) { if (!BlockedVoxels.ContainsKey(Voxel.EntityId)) { BlockedVoxels.Add(Voxel.EntityId, new List <Vector3I>()); } } Vector3D hitpos = VoxelHit.Value; //MyVoxelMaterialDefinition Material = Voxel.GetMaterialAt(ref hitpos); Vector3D CutoutSphereCenter = hitpos + (-WorkingRay.Direction * DrillingOffsetM); //stopwatch.Stop(); //WriteToLog("ProcessVoxels", $"Hit: {Math.Round(Vector3D.Distance(WorkingRay.From, hitpos), 2)}m, cutout: {Math.Round(Vector3D.Distance(WorkingRay.From, CutoutSphereCenter), 2)} m away, Material: {(Material != null ? Material.MaterialTypeName : "null")}"); //stopwatch.Start(); BoundingSphereD Cutout = new BoundingSphereD(CutoutSphereCenter, CutoutRadius); //List<Vector3I> VoxelPoints = Voxel.GetVoxelPointsInSphere(Cutout); Vector3I refCorner; Vector3I refMaxCorner; MyStorageData cutoutVoxels = Voxel.GetVoxelCacheInSphere(Cutout, out refCorner, out refMaxCorner); //WriteToLog("ProcessVoxels", $"Cutout cache Min is {Math.Round(Vector3D.Distance(hitpos, Voxel.PositionLeftBottomCorner + refCorner), 2)}m away"); //WriteToLog("ProcessVoxels", $"Cutout cache Max is {Math.Round(Vector3D.Distance(hitpos, Voxel.PositionLeftBottomCorner + refCorner + cutoutVoxels.Size3D), 2)}m away"); Dictionary <MyVoxelMaterialDefinition, float> Materials = new Dictionary <MyVoxelMaterialDefinition, float>(); int nullMaterials = 0; int totalPoints = 0; Stopwatch enumeratorWatch = new Stopwatch(); using (MyStorageData.MortonEnumerator VoxelLoop = new MyStorageData.MortonEnumerator(cutoutVoxels, MyStorageDataTypeEnum.Content | MyStorageDataTypeEnum.Material)) { MyKernel.ResponderModule.UpdateStatusReport($"Entered enumerator", true); enumeratorWatch.Start(); Dictionary <byte, float> RawMaterials = new Dictionary <byte, float>(); Vector3I VoxelInSphereCenter; Vector3 CoordsSystemOutValue; //MyVoxelCoordSystems.WorldPositionToLocalPosition(Cutout.Center, Voxel.PositionComp.WorldMatrix, Voxel.PositionComp.WorldMatrixInvScaled, Voxel.SizeInMetresHalf, out CoordsSystemOutValue); //VoxelInSphereCenter = new Vector3I(CoordsSystemOutValue / 1f) + Voxel.StorageMin; Vector3D sphereCenter = Cutout.Center; MyVoxelCoordSystems.WorldPositionToLocalPosition(Voxel.PositionLeftBottomCorner, ref sphereCenter, out CoordsSystemOutValue); VoxelInSphereCenter = new Vector3I(CoordsSystemOutValue / 1f) + Voxel.StorageMin; Vector3D ReconstructedPosition = Voxel.PositionLeftBottomCorner + VoxelInSphereCenter; //WriteToLog("ProcessVoxels", $"VoxelInSphereCenter is {Math.Round(Vector3D.Distance(hitpos, ReconstructedPosition), 2)}m away from hitpos"); double CutoutRadiusSquared = Cutout.Radius * Cutout.Radius; int processedPoints = 0; int skippedPoints = 0; int linearIndex = -1; for (bool move = true; move != false;) { enumeratorWatch.Start(); try { move = VoxelLoop.MoveNext(); } catch { move = false; MyKernel.ResponderModule.UpdateStatusReport("Keen enumerator died", true); } if (move) { linearIndex++; Vector3I voxelPoint; cutoutVoxels.ComputePosition(linearIndex, out voxelPoint); voxelPoint += refCorner; BoundingBoxD voxelBox = new BoundingBoxD(Voxel.PositionLeftBottomCorner + voxelPoint, Voxel.PositionLeftBottomCorner + voxelPoint + 1); var PointContainment = Cutout.Contains(voxelBox); bool Contacts = PointContainment.HasFlag(ContainmentType.Contains) || PointContainment.HasFlag(ContainmentType.Intersects); if (Contacts || Vector3D.DistanceSquared(ReconstructedPosition, Voxel.PositionLeftBottomCorner + voxelPoint) <= CutoutRadiusSquared) { bool pointBlocked = true; lock (BlockedVoxels) { pointBlocked = BlockedVoxels[Voxel.EntityId].Contains(voxelPoint); if (!pointBlocked) { BlockedVoxels[Voxel.EntityId].Add(voxelPoint); } } if (!pointBlocked) { byte ContentFillLevel = cutoutVoxels.Content(linearIndex); byte MaterialId = cutoutVoxels.Material(linearIndex); float MaterialFillRatio = ContentFillLevel / MyVoxelConstants.VOXEL_CONTENT_FULL_FLOAT; if (!RawMaterials.ContainsKey(MaterialId)) { RawMaterials.Add(MaterialId, MaterialFillRatio); } else { RawMaterials[MaterialId] += MaterialFillRatio; } processedPoints++; } else { skippedPoints++; } } else { skippedPoints++; } int voxelIterationLimit = MyKernel.Session.Settings.SpeedMultiplierAcceleratesDrilling ? VoxelIterationLimit * MyKernel.TermControls.SpeedMultiplier : VoxelIterationLimit; if (processedPoints > 0 && processedPoints % voxelIterationLimit == 0) { //MyKernel.ResponderModule.UpdateStatusReport($"Cutting out {Math.Round(Cutout.Radius * 2, 1)}m sphere:\r\n{linearIndex} voxels processed", append: true); enumeratorWatch.Stop(); yield return(true); } } totalPoints = linearIndex; } MyKernel.ResponderModule.UpdateStatusReport($"Total processed points: {totalPoints} ({processedPoints}/{skippedPoints})", true); enumeratorWatch.Stop(); MyKernel.ResponderModule.UpdateStatusReport($"EnumeratorWatch: {Math.Round(enumeratorWatch.Elapsed.TotalMilliseconds, 3)}ms", true); //WriteToLog("ProcessVoxels", $"Found {processedPoints} valid voxel points out of {totalPoints}", EemRdx.SessionModules.LoggingLevelEnum.DebugLog, true, 2000); foreach (KeyValuePair <byte, float> kvp in RawMaterials) { MyVoxelMaterialDefinition material = MyDefinitionManager.Static.GetVoxelMaterialDefinition(kvp.Key); if (material != null) { Materials.Add(material, kvp.Value * DrillingYield); } else { nullMaterials++; continue; } } } MyKernel.ResponderModule.UpdateStatusReport($"Quitted enumerator", true); //WriteToLog("ProcessVoxels", $"Found {VoxelPoints.Count} valid voxel points", EemRdx.SessionModules.LoggingLevelEnum.DebugLog, true, 2000); //WriteToLog("ProcessVoxels", $"Found {Materials.Count} valid materials{(nullMaterials > 0 ? $" and {nullMaterials} null materials" : "")}", EemRdx.SessionModules.LoggingLevelEnum.DebugLog, true, 2000); //foreach (var kvp in Materials) //{ // WriteToLog("ProcessVoxels", $"Material: {kvp.Key.MaterialTypeName} (mined ore: {(kvp.Key.MinedOre != null ? kvp.Key.MinedOre : "null")}), amount: {kvp.Value}"); //} MyKernel.ResponderModule.UpdateStatusReport($"Calculating materials", true); Dictionary <MyObjectBuilder_Ore, float> MaterialsToAdd = new Dictionary <MyObjectBuilder_Ore, float>(); int nullMinedOres = 0; foreach (KeyValuePair <MyVoxelMaterialDefinition, float> kvp in Materials) { MyVoxelMaterialDefinition material = kvp.Key; if (string.IsNullOrWhiteSpace(material.MinedOre)) { nullMinedOres++; continue; } try { if (MyKernel.TermControls.DumpStone && material.MinedOre == "Stone") { continue; } MyObjectBuilder_Ore oreBuilder = MyObjectBuilderSerializer.CreateNewObject <MyObjectBuilder_Ore>(material.MinedOre); oreBuilder.MaterialTypeName = new MyStringHash?(material.Id.SubtypeId); MyPhysicalItemDefinition oreDef = MyDefinitionManager.Static.GetPhysicalItemDefinition(oreBuilder); float MinedAmountInKg = (kvp.Value / 1000 / oreDef.Volume) * oreDef.Mass * kvp.Key.MinedOreRatio * 32; stopwatch.Stop(); //WriteToLog("ProcessVoxels", $"Found material {material.MaterialTypeName}, {Math.Round(kvp.Value)} points, mined amount would be {Math.Round(MinedAmountInKg)} kg"); stopwatch.Start(); MaterialsToAdd.Add(oreBuilder, MinedAmountInKg); } catch (Exception Scrap) { LogError("ProcessVoxels().dict_Materials.Iterate", $"Unknown error occurred on material {material.MinedOre}", Scrap); } } Stopwatch cutoutWatch = Stopwatch.StartNew(); //Voxel.Storage.WriteRange(cutoutVoxels, MyStorageDataTypeFlags.ContentAndMaterial, refCorner - 1, refMaxCorner + 1); Voxel.RootVoxel.PerformCutOutSphereFast(Cutout.Center, (float)Cutout.Radius, true); cutoutWatch.Stop(); //WriteToLog("ProcessVoxels", $"Sphere was cut in {cutoutWatch.ElapsedTicks} ticks, which is {Math.Round(cutoutWatch.Elapsed.TotalMilliseconds, 4)} ms"); foreach (var kvp in MaterialsToAdd) { MyDefinitionId OreId = kvp.Key.GetId(); MyItemType oreItemType = new MyItemType(OreId.TypeId, OreId.SubtypeId); float amountToAdd = kvp.Value; while (amountToAdd > 0.1f) { while (((float)ToolCargo.CurrentVolume / (float)ToolCargo.MaxVolume) > 0.9f) { yield return(true); } MyInventoryItem oreItem = new MyInventoryItem(oreItemType, 0, (VRage.MyFixedPoint)amountToAdd); float FittingAmount = (float)(ToolCargo as Sandbox.Game.MyInventory).ComputeAmountThatFits(OreId); if (FittingAmount > amountToAdd) { FittingAmount = amountToAdd; } ToolCargo.AddItems((VRage.MyFixedPoint)FittingAmount, kvp.Key); amountToAdd -= FittingAmount; //MyKernel.ResponderModule.UpdateStatusReport($"Adding {Math.Round(FittingAmount)}u of {OreId.SubtypeId}, {amountToAdd} remains", append: false); } } } else { MyKernel.ResponderModule.UpdateStatusReport($"No voxel found", true); } } stopwatch.Stop(); double ElapsedMs = stopwatch.Elapsed.TotalMilliseconds; //WriteToLog("ProcessVoxels", $"Voxels processed, elapsed time: {stopwatch.ElapsedTicks} ticks, which is {Math.Round(ElapsedMs, 3)} ms"); MyKernel.ResponderModule.UpdateStatusReport($"Cycle finished", true); yield return(false); }