1919import meteordevelopment .meteorclient .utils .misc .Keybind ;
2020import meteordevelopment .meteorclient .utils .misc .Names ;
2121import meteordevelopment .meteorclient .utils .misc .input .KeyAction ;
22- import meteordevelopment .meteorclient .utils .player .PlayerUtils ;
2322import meteordevelopment .meteorclient .utils .player .Rotations ;
2423import meteordevelopment .meteorclient .utils .render .RenderUtils ;
24+ import meteordevelopment .meteorclient .utils .render .color .Color ;
2525import meteordevelopment .meteorclient .utils .render .color .SettingColor ;
2626import meteordevelopment .meteorclient .utils .world .BlockIterator ;
2727import meteordevelopment .meteorclient .utils .world .BlockUtils ;
2828import meteordevelopment .orbit .EventHandler ;
2929import meteordevelopment .orbit .EventPriority ;
3030import net .minecraft .block .Block ;
31+ import net .minecraft .block .BlockState ;
3132import net .minecraft .network .packet .c2s .play .HandSwingC2SPacket ;
3233import net .minecraft .network .packet .c2s .play .PlayerActionC2SPacket ;
3334import net .minecraft .util .Hand ;
3637import net .minecraft .util .math .BlockPos ;
3738import net .minecraft .util .math .Box ;
3839import net .minecraft .util .math .Direction ;
40+ import net .minecraft .util .math .MathHelper ;
3941import net .minecraft .util .math .Vec3d ;
42+ import net .minecraft .util .shape .VoxelShape ;
4043import net .minecraft .world .RaycastContext ;
4144
4245import java .util .ArrayList ;
4346import java .util .Comparator ;
4447import java .util .List ;
4548import java .util .Set ;
4649
50+ import org .joml .Random ;
51+
52+ import com .google .common .collect .HashMultimap ;
53+ import com .google .common .collect .Multimap ;
54+
4755public class Nuker extends Module {
4856 private final SettingGroup sgGeneral = settings .getDefaultGroup ();
4957 private final SettingGroup sgWhitelist = settings .createGroup ("Whitelist" );
@@ -187,6 +195,14 @@ public class Nuker extends Module {
187195 .build ()
188196 );
189197
198+ private final Setting <Boolean > smoothRotate = sgGeneral .add (new BoolSetting .Builder ()
199+ .name ("smooth-rotate" )
200+ .description ("Smoothes rotation according to delay." )
201+ .defaultValue (true )
202+ .visible (() -> rotate .get () && maxBlocksPerTick .get () <= 1 )
203+ .build ()
204+ );
205+
190206 // Whitelist and blacklist
191207
192208 private final Setting <ListMode > listMode = sgWhitelist .add (new EnumSetting .Builder <ListMode >()
@@ -285,15 +301,54 @@ public class Nuker extends Module {
285301 .build ()
286302 );
287303
304+ private final Setting <Boolean > enableRenderDebug = sgRender .add (new BoolSetting .Builder ()
305+ .name ("debug-visuals" )
306+ .description ("Enable debug rendering." )
307+ .defaultValue (false )
308+ .build ()
309+ );
310+
288311 private final List <BlockPos > blocks = new ArrayList <>();
312+ private BlockPos currentBlock = null ;
289313 private final Set <BlockPos > interacted = new ObjectOpenHashSet <>();
290314
291315 private boolean firstBlock ;
292316 private final BlockPos .Mutable lastBlockPos = new BlockPos .Mutable ();
293317
294318 private int timer ;
319+ private int timerMax ;
320+ private double startYaw = 0 ;
321+ private double startPitch = 0 ;
322+ private Vec3d randomOffsetOnFace ;
295323 private int noBlockTimer ;
296324
325+ private void resetOffsetOnFace (BlockPos blockPos ) {
326+ // Check if the offset has already been reset for this block
327+ if (lastBlockPos .equals (blockPos )) return ;
328+
329+ // Select a random visible side of the block
330+ ArrayList <Direction > sideList = new ArrayList <>(visibleFaceMap .get (blockPos ));
331+ Direction side = sideList .get (new Random ().nextInt (sideList .size ()));
332+
333+ // <narakomii> this seems to break when i remove this scope, i probably did something dumb and didn't notice - i'm too lazy to fix it rn
334+ {
335+ // Select a random offset
336+ double x = Math .random ();
337+ double y = Math .random ();
338+ double z = Math .random ();
339+
340+ // Clamp the offset to the selected side
341+ if (side .getOffsetX () == 1 ) x = 1 ;
342+ else if (side .getOffsetX () == -1 ) x = 0 ;
343+ else if (side .getOffsetY () == 1 ) y = 1 ;
344+ else if (side .getOffsetY () == -1 ) y = 0 ;
345+ else if (side .getOffsetZ () == 1 ) z = 1 ;
346+ else if (side .getOffsetZ () == -1 ) z = 0 ;
347+
348+ randomOffsetOnFace = new Vec3d (x , y , z );
349+ }
350+ }
351+
297352 private final BlockPos .Mutable pos1 = new BlockPos .Mutable (); // Rendering for cubes
298353 private final BlockPos .Mutable pos2 = new BlockPos .Mutable ();
299354 int maxh = 0 ;
@@ -305,9 +360,11 @@ public Nuker() {
305360
306361 @ Override
307362 public void onActivate () {
363+ currentBlock = null ;
308364 firstBlock = true ;
309365 timer = 0 ;
310366 noBlockTimer = 0 ;
367+ lastBlockPos .set ((int ) Math .floor (mc .player .getEyePos ().x ), (int ) Math .floor (mc .player .getEyePos ().y ), (int ) Math .floor (mc .player .getEyePos ().z ));
311368 interacted .clear ();
312369 }
313370
@@ -342,10 +399,13 @@ private void onTickPre(TickEvent.Pre event) {
342399 // Update timer
343400 if (timer > 0 ) {
344401 timer --;
345- return ;
346402 }
347403
404+ visibleFaceMap .clear ();
405+
348406 // Calculate some stuff
407+ boolean smoothRotateEnabled = rotate .get () && maxBlocksPerTick .get () <= 1 && smoothRotate .get ();
408+
349409 double pX = mc .player .getX (), pY = mc .player .getY (), pZ = mc .player .getZ ();
350410 double rangeSq = Math .pow (range .get (), 2 );
351411 BlockPos playerBlockPos = mc .player .getBlockPos ();
@@ -427,15 +487,15 @@ private void onTickPre(TickEvent.Pre event) {
427487 // Block must be breakable
428488 if (!BlockUtils .canBreak (blockPos , blockState ) && !interact .get ()) return ;
429489
430- // Raycast to block
431- if (isOutOfRange (blockPos )) return ;
432-
433490 // Check whitelist or blacklist
434491 if (listMode .get () == ListMode .Whitelist && !whitelist .get ().contains (blockState .getBlock ())) return ;
435492 if (listMode .get () == ListMode .Blacklist && blacklist .get ().contains (blockState .getBlock ())) return ;
436493
437494 if (interact .get () && interacted .contains (blockPos )) return ;
438495
496+ // Raycast to block
497+ if (isOutOfRange (blockPos , blockState )) return ;
498+
439499 // Add block
440500 blocks .add (blockPos .toImmutable ());
441501 });
@@ -445,28 +505,53 @@ private void onTickPre(TickEvent.Pre event) {
445505 // Sort blocks
446506 if (sortMode .get () == SortMode .TopDown )
447507 blocks .sort (Comparator .comparingDouble (value -> -value .getY ()));
448- else if (sortMode .get () != SortMode .None )
449- blocks .sort (Comparator .comparingDouble (value -> Utils .squaredDistance (pX , pY , pZ , value .getX () + 0.5 , value .getY () + 0.5 , value .getZ () + 0.5 ) * (sortMode .get () == SortMode .Closest ? 1 : -1 )));
450-
451- // Check if some block was found
508+ else if (sortMode .get () == SortMode .ClosestToLast && noBlockTimer <= 0 ) {
509+ // Sort by closest to last mined block, then closest to player
510+ blocks .sort (Comparator .comparingDouble (value ->
511+ Utils .squaredDistance ((double ) lastBlockPos .getX () + 0.5 , (double ) lastBlockPos .getY () + 0.5 , (double ) lastBlockPos .getZ () + 0.5 , (double ) value .getX () + 0.5 , (double ) value .getY () + 0.5 , (double ) value .getZ () + 0.5 )
512+ + Utils .squaredDistance (pX , pY , pZ , (double ) value .getX () + 0.5 , (double ) value .getY () + 0.5 , (double ) value .getZ () + 0.5 ) / 262144
513+ ));
514+ } else if (sortMode .get () != SortMode .None )
515+ blocks .sort (Comparator .comparingDouble (value -> Utils .squaredDistance (pX , pY , pZ , value .getX () + 0.5 , value .getY () + 0.5 , value .getZ () + 0.5 ) * (sortMode .get () == SortMode .Furthest ? -1 : 1 )));
516+
517+ // Check if no block was found
452518 if (blocks .isEmpty ()) {
453- interacted .clear ();
454519 // If no block was found for long enough then set firstBlock flag to true to not wait before breaking another again
455- if (noBlockTimer ++ >= delay .get ()) firstBlock = true ;
520+ if (!smoothRotateEnabled && noBlockTimer ++ >= delay .get ()) firstBlock = true ;
521+
522+ // Reset some values
523+ currentBlock = null ;
524+ interacted .clear ();
525+ startYaw = mc .player .getYaw ();
526+ startPitch = mc .player .getPitch ();
456527 return ;
457528 }
458529 else {
459530 noBlockTimer = 0 ;
460531 }
461532
533+ // Check if a block is already being mined
534+ if (currentBlock != null ) {
535+ // Check if it's still valid
536+ if (!BlockUtils .canInstaBreak (currentBlock ) && !packetMine .get () && blocks .contains (currentBlock )) {
537+ // Move it to the start of the list (will be iterated first)
538+ blocks .remove (currentBlock );
539+ blocks .addFirst (currentBlock );
540+ } else {
541+ currentBlock = null ;
542+ }
543+ }
544+
462545 // Update timer
463546 if (!firstBlock && !lastBlockPos .equals (blocks .getFirst ())) {
464- timer = delay .get ();
547+ timer = timerMax = delay .get ();
465548
466549 firstBlock = false ;
467- lastBlockPos .set (blocks .getFirst ());
468550
469- if (timer > 0 ) return ;
551+ // Try to reset random face offset
552+ resetOffsetOnFace (blocks .getFirst ());
553+
554+ lastBlockPos .set (blocks .getFirst ());
470555 }
471556
472557 // Break
@@ -477,12 +562,43 @@ else if (sortMode.get() != SortMode.None)
477562
478563 boolean canInstaMine = BlockUtils .canInstaBreak (block );
479564
480- if (rotate .get ()) Rotations .rotate (Rotations .getYaw (block ), Rotations .getPitch (block ), () -> breakBlock (block ));
481- else breakBlock (block );
565+ resetOffsetOnFace (block );
566+ // Add the offset to the block's position
567+ Vec3d lookPos = new Vec3d (block ).add (randomOffsetOnFace );
568+
569+ if (enableRenderDebug .get ()) {
570+ // Display currently targeted point
571+ RenderUtils .renderTickingPoint (lookPos , Color .GREEN , 1 , false );
572+ }
573+
574+ if (timer <= 0 ) {
575+ // If delay is over, mine the block
576+ if (rotate .get ())
577+ Rotations .rotate (startYaw = Rotations .getYaw (lookPos ), startPitch = Rotations .getPitch (lookPos ), () -> breakBlock (block ));
578+ else
579+ breakBlock (block );
580+ } else if (smoothRotateEnabled ) {
581+ // If not and smooth rotate is enabled, lerp to the target
582+ double endYaw = Rotations .getYaw (lookPos );
583+ double endPitch = Rotations .getPitch (lookPos );
584+ double delta = 1 - ((double ) timer ) / timerMax ;
585+
586+ // Weird easing hybrid of EaseOutQuart and modified EaseOutBack
587+ final double c = 0.37 ;
588+ delta = (1 + (c + 1 ) * Math .pow (delta - 1 , 3 ) + c * Math .pow (delta - 1 , 2 )) * (1 - Math .pow (1 - delta , 4 ));
589+
590+ double yaw = MathHelper .lerpAngleDegrees (delta , startYaw , endYaw );
591+ double pitch = MathHelper .lerpAngleDegrees (delta , startPitch , endPitch );
592+
593+ Rotations .rotate (yaw , pitch );
594+ break ;
595+ }
482596
483597 if (enableRenderBreaking .get ()) RenderUtils .renderTickingBlock (block , sideColor .get (), lineColor .get (), shapeModeBreak .get (), 0 , 8 , true , false );
484598 lastBlockPos .set (block );
485599
600+ currentBlock = block ;
601+
486602 count ++;
487603 if (!canInstaMine && !packetMine .get () /* With packet mine attempt to break everything possible at once */ ) break ;
488604 }
@@ -513,14 +629,46 @@ private void breakBlock(BlockPos blockPos) {
513629 }
514630 }
515631
516- private boolean isOutOfRange (BlockPos blockPos ) {
517- Vec3d pos = blockPos .toCenterPos ();
518- RaycastContext raycastContext = new RaycastContext (mc .player .getEyePos (), pos , RaycastContext .ShapeType .COLLIDER , RaycastContext .FluidHandling .NONE , mc .player );
519- BlockHitResult result = mc .world .raycast (raycastContext );
520- if (result == null || !result .getBlockPos ().equals (blockPos ))
521- return !PlayerUtils .isWithin (pos , wallsRange .get ());
632+ private Multimap <BlockPos , Direction > visibleFaceMap = HashMultimap .create ();
633+
634+ private static final double boxShrink = Math .pow (2 , -16 );
635+
636+ private boolean isOutOfRange (BlockPos blockPos , BlockState blockState ) {
637+ boolean allOutOfRange = true ;
638+
639+ VoxelShape shape = blockState .getOutlineShape (mc .player .getEntityWorld (), blockPos );
640+
641+ // Iterate over each cuboid of the block's interaction box
642+ for (Box box : shape .getBoundingBoxes ()) {
643+ // Shrink cuboid by a tiny amount to avoid false misses, but preserve accuracy
644+ // Then offset by block position
645+ box = box .contract (boxShrink ).offset (blockPos );
646+
647+ // Iterate over each corner of the adjusted cuboid
648+ for (Vec3d corner : Utils .getBoxCorners (box )) {
649+ // Raycast to the corner
650+ RaycastContext raycastContext = new RaycastContext (mc .player .getEyePos (), corner , RaycastContext .ShapeType .COLLIDER , RaycastContext .FluidHandling .NONE , mc .player );
651+ BlockHitResult result = mc .world .raycast (raycastContext );
652+
653+ boolean outOfRange = result == null || !result .getBlockPos ().equals (blockPos );
654+
655+ if (!outOfRange ) {
656+ // If raycast hit, add the block face to the map
657+ visibleFaceMap .put (blockPos .toImmutable (), result .getSide ());
658+
659+ // Then, if debug rendering is disabled: break and return false immediately
660+ // If enabled: set return value to false and draw the corner
661+ if (!enableRenderDebug .get ()) {
662+ return false ;
663+ } else {
664+ allOutOfRange = false ;
665+ RenderUtils .renderTickingPoint (corner , Color .BLUE , 1 , false );
666+ }
667+ }
668+ }
669+ }
522670
523- return false ;
671+ return allOutOfRange ;
524672 }
525673
526674 private void addTargetedBlockToList () {
@@ -564,7 +712,8 @@ public enum SortMode {
564712 None ,
565713 Closest ,
566714 Furthest ,
567- TopDown
715+ TopDown ,
716+ ClosestToLast
568717 }
569718
570719 public enum Shape {
@@ -580,4 +729,4 @@ public static int chebyshevDist(int x1, int y1, int z1, int x2, int y2, int z2)
580729 int dZ = Math .abs (z2 - z1 );
581730 return Math .max (Math .max (dX , dY ), dZ );
582731 }
583- }
732+ }
0 commit comments