@@ -63,9 +63,11 @@ public final class MaceDmgHack extends Hack
6363 private static final double MIN_FALL = 1.6 ;
6464 private static final double SCAN_STEP = 0.25 ;
6565 private static final int CONFIRM_TIMEOUT_TICKS = 20 ;
66- private static final int RECOVERY_TICKS = 2 ;
66+ private static final int TOTEM_POP_FALLBACK_TICKS = 2 ;
67+ private static final int SAFE_RECOVERY_TICKS = 2 ;
68+ private static final int UNSAFE_RECOVERY_TICKS = 6 ;
6769 private static final int TOTEM_BYPASS_GRACE_TICKS = 40 ;
68- private static final double FABRIC_MAX_HEIGHT = 22.0 ;
70+ private static final double FABRIC_MAX_HEIGHT = 22.3 ;
6971 private static final double PAPER_MAX_HEIGHT = 50.0 ;
7072
7173 private enum ServerType
@@ -113,8 +115,9 @@ public void setSelected(ServerType selected)
113115 @ Override
114116 public void update ()
115117 {
116- if (isChecked ())
118+ if (isChecked () && ! autoOptimizeWasChecked )
117119 applyAutoOptimize ();
120+ autoOptimizeWasChecked = isChecked ();
118121 }
119122 };
120123
@@ -190,6 +193,7 @@ public void update()
190193 private volatile int recentTotemPopEntityId = -1 ;
191194 private volatile int recentTotemPopTicks ;
192195 private volatile boolean safetyListenersRegistered ;
196+ private boolean autoOptimizeWasChecked ;
193197 private final Map <UUID , TotemBypassState > totemBypassSteps =
194198 new HashMap <>();
195199 private final ArrayDeque <Integer > queuedTargetIds = new ArrayDeque <>();
@@ -236,7 +240,7 @@ public String getRenderName()
236240 {
237241 double h = base + i * inc ;
238242 if (fabric )
239- h = Math .min (h , 22.0 );
243+ h = Math .min (h , 22.3 );
240244 sb .append (i == 0 ? " " : "\u2192 " ).append (String .format ("%.0f" , h ));
241245 }
242246
@@ -247,6 +251,7 @@ public String getRenderName()
247251 @ Override
248252 protected void onEnable ()
249253 {
254+ autoOptimizeWasChecked = false ;
250255 onServerTypeChanged ();
251256 EVENTS .add (PlayerAttacksEntityListener .class , this );
252257 registerSafetyListeners ();
@@ -255,6 +260,7 @@ protected void onEnable()
255260 @ Override
256261 protected void onDisable ()
257262 {
263+ autoOptimizeWasChecked = false ;
258264 EVENTS .remove (PlayerAttacksEntityListener .class , this );
259265 totemBypassSteps .clear ();
260266 clearQueuedTargets ();
@@ -439,6 +445,15 @@ public void onReceivedPacket(PacketInputEvent event)
439445 recentTotemPopEntityId = poppedPlayer .getId ();
440446 recentTotemPopTicks = 2 ;
441447 armTotemBypassBurst (poppedPlayer );
448+ if (smashState == SmashState .WAITING_FOR_MACE_CONFIRM
449+ && poppedPlayer .getId () == pendingTargetId )
450+ {
451+ // Totem-pop shows the burst resolved, but not that the server
452+ // safely cleared our fake fall state. Cut the wait short
453+ // without
454+ // releasing fall debt immediately.
455+ confirmTicks = Math .min (confirmTicks , TOTEM_POP_FALLBACK_TICKS );
456+ }
442457 }
443458
444459 if ((smashState != SmashState .WAITING_FOR_MACE_CONFIRM
@@ -455,21 +470,30 @@ public void onReceivedPacket(PacketInputEvent event)
455470 damage .sourceType ().is (DamageTypes .PLAYER_ATTACK );
456471 if (!confirmedSmash && !confirmedRejectedSpoof )
457472 return ;
458-
459- if (confirmedSmash && smashSparkles .isChecked () && MC .level != null )
460- {
461- int targetId = damage .entityId ();
462- MC .execute (() -> spawnSmashPartyEffects (targetId ));
463- }
464-
473+
465474 // MACE_SMASH proves that postHurtEnemy() reset server fall distance.
466475 // PLAYER_ATTACK from the same pending mace attack proves that the
467476 // server
468477 // evaluated canSmashAttack() as false, so dangerous fake fall distance
469478 // was not retained. Either result safely ends this attempt.
479+ confirmCurrentSmash (confirmedSmash );
480+ }
481+
482+ private void confirmCurrentSmash (boolean spawnSparkles )
483+ {
484+ if (spawnSparkles && smashSparkles .isChecked () && MC .level != null
485+ && pendingTargetId != -1 )
486+ {
487+ int targetId = pendingTargetId ;
488+ if (!MC .isSameThread ())
489+ MC .execute (() -> spawnSmashPartyEffects (targetId ));
490+ else
491+ spawnSmashPartyEffects (targetId );
492+ }
493+
470494 smashState = SmashState .CONFIRMED_SAFE ;
471495 confirmTicks = 0 ;
472- recoveryTicks = RECOVERY_TICKS ;
496+ recoveryTicks = SAFE_RECOVERY_TICKS ;
473497 }
474498
475499 @ Override
@@ -479,7 +503,7 @@ public void onUpdate()
479503 && --confirmTicks <= 0 )
480504 {
481505 smashState = SmashState .FAILED_UNSAFE ;
482- recoveryTicks = RECOVERY_TICKS ;
506+ recoveryTicks = UNSAFE_RECOVERY_TICKS ;
483507 }
484508
485509 if (recentTotemPopTicks > 0 && --recentTotemPopTicks <= 0 )
@@ -569,9 +593,13 @@ private void executeQueuedTarget(Entity target)
569593 try
570594 {
571595 selectQueuedMaceSlot (maceSlot );
572- performQueuedSmashBurst (target );
596+ boolean burstSent = performQueuedSmashBurst (target );
597+ if (burstSent )
598+ restoreQueuedSlot ();
573599 }finally
574600 {
601+ if (queuedPreviousSlot != -1 )
602+ restoreQueuedSlot ();
575603 queuedAttackInProgress = false ;
576604 }
577605 }
@@ -829,6 +857,7 @@ private void applyAutoOptimize()
829857 height .setValue (FABRIC_MAX_HEIGHT );
830858 break ;
831859 }
860+ autoOptimizeWasChecked = true ;
832861 }
833862
834863 private void onServerTypeChanged ()
@@ -857,6 +886,12 @@ private void sendFakeY(double offset)
857886
858887 private void spawnSmashPartyEffects (int entityId )
859888 {
889+ if (!MC .isSameThread ())
890+ {
891+ MC .execute (() -> spawnSmashPartyEffects (entityId ));
892+ return ;
893+ }
894+
860895 if (!isEnabled () || !smashSparkles .isChecked () || MC .level == null )
861896 return ;
862897
0 commit comments