From 523e7cac8029385417ca5f2c831bb957180bc7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=A8=E8=91=89=20Scarlet?= <93977077+mukjepscarlet@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:52:02 +0800 Subject: [PATCH] fix(SwingMode): follow 26.3 swing animation and packet rules 26.3 moved the swing packet out of LocalPlayer.swing into the attack paths: interaction swings are predicted locally and broadcast by the server from its own interaction result, while attacks still need `ServerboundPunchPacket`. SwingMode now exposes attackSwing (attack animation + packet) and interactSwing (interact animation, never a packet), with `of(boolean)` as an adapter for the existing boolean call sites. Every call site is reclassified, which also fixes the wrong animation on interaction and block-breaking paths, spurious punch packets after interactions, swing checks that accepted `Fail`/`SERVER_ONLY` results, and attack swings that stopped notifying the server after the 26.3 migration. --- .../systems/modules/combat/AutoCity.java | 5 +-- .../systems/modules/combat/Burrow.java | 2 - .../systems/modules/combat/CrystalAura.java | 29 ++----------- .../systems/modules/combat/KillAura.java | 3 +- .../systems/modules/misc/Notebot.java | 23 +++++----- .../systems/modules/player/AntiAFK.java | 3 +- .../systems/modules/player/GhostHand.java | 9 ++-- .../systems/modules/world/HighwayBuilder.java | 2 +- .../systems/modules/world/Nuker.java | 5 +-- .../systems/modules/world/VeinMiner.java | 3 +- .../meteorclient/utils/player/SwingMode.java | 43 +++++++++++++++++++ .../meteorclient/utils/world/BlockUtils.java | 10 ++--- 12 files changed, 80 insertions(+), 57 deletions(-) create mode 100644 src/main/java/meteordevelopment/meteorclient/utils/player/SwingMode.java diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/AutoCity.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/AutoCity.java index cf1c6e1acab..b2d857b9453 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/AutoCity.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/AutoCity.java @@ -18,13 +18,13 @@ import meteordevelopment.meteorclient.utils.player.InvUtils; import meteordevelopment.meteorclient.utils.player.PlayerUtils; import meteordevelopment.meteorclient.utils.player.Rotations; +import meteordevelopment.meteorclient.utils.player.SwingMode; import meteordevelopment.meteorclient.utils.render.color.SettingColor; import meteordevelopment.meteorclient.utils.world.BlockUtils; import meteordevelopment.orbit.EventHandler; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; -import net.minecraft.network.protocol.game.ServerboundPunchPacket; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Items; @@ -216,8 +216,7 @@ public void mine(boolean done) { mc.gameMode.startPrediction(mc.level, sequence -> new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, targetPos, direction, sequence)); mc.gameMode.startPrediction(mc.level, sequence -> new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK, targetPos, direction, sequence)); - if (swingHand.get()) mc.player.swing(InteractionHand.MAIN_HAND, mc.player.getItemInHand(InteractionHand.MAIN_HAND).getAttackAnimation(), false); - else mc.getConnection().send(new ServerboundPunchPacket()); + SwingMode.of(swingHand.get()).attackSwing(InteractionHand.MAIN_HAND); if (switchMode.get() == SwitchMode.Silent) InvUtils.swapBack(); } diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/Burrow.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/Burrow.java index f7504d45772..f33005af882 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/Burrow.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/Burrow.java @@ -22,7 +22,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; -import net.minecraft.network.protocol.game.ServerboundPunchPacket; import net.minecraft.world.InteractionHand; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Items; @@ -200,7 +199,6 @@ private void burrow() { InvUtils.swap(block.slot(), true); mc.gameMode.useItemOn(mc.player, InteractionHand.MAIN_HAND, new BlockHitResult(Utils.vec3(blockPos), Direction.UP, blockPos, false)); - mc.player.connection.send(new ServerboundPunchPacket()); InvUtils.swapBack(); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/CrystalAura.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/CrystalAura.java index 87097ea3068..66bd2f59abf 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/CrystalAura.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/CrystalAura.java @@ -26,10 +26,7 @@ import meteordevelopment.meteorclient.utils.entity.EntityUtils; import meteordevelopment.meteorclient.utils.entity.Target; import meteordevelopment.meteorclient.utils.misc.Keybind; -import meteordevelopment.meteorclient.utils.player.FindItemResult; -import meteordevelopment.meteorclient.utils.player.InvUtils; -import meteordevelopment.meteorclient.utils.player.PlayerUtils; -import meteordevelopment.meteorclient.utils.player.Rotations; +import meteordevelopment.meteorclient.utils.player.*; import meteordevelopment.meteorclient.utils.render.NametagUtils; import meteordevelopment.meteorclient.utils.render.RenderUtils; import meteordevelopment.meteorclient.utils.render.color.Color; @@ -871,11 +868,7 @@ private void attackCrystal(Entity entity) { // Attack mc.player.connection.send(new ServerboundAttackPacket(entity.getId())); - InteractionHand hand = InvUtils.findInHotbar(Items.END_CRYSTAL).getHand(); - if (hand == null) hand = InteractionHand.MAIN_HAND; - - if (swingMode.get().client()) mc.player.swing(hand, mc.player.getItemInHand(hand).getAttackAnimation(), false); - if (swingMode.get().packet()) mc.getConnection().send(new ServerboundPunchPacket()); + swingMode.get().attackSwing(InteractionHand.MAIN_HAND); attacks++; } @@ -1037,8 +1030,7 @@ private void placeCrystal(BlockHitResult result, double damage, BlockPos support // Place crystal mc.gameMode.startPrediction(mc.level, sequence -> new ServerboundUseItemOnPacket(hand, result, sequence)); - if (swingMode.get().client()) mc.player.swing(hand, mc.player.getItemInHand(hand).getAttackAnimation(), false); - if (swingMode.get().packet()) mc.getConnection().send(new ServerboundPunchPacket()); + swingMode.get().interactSwing(hand); placing = true; placingTimer = 4; @@ -1366,21 +1358,6 @@ public boolean matches(PauseMode process) { } } - public enum SwingMode { - Both, - Packet, - Client, - None; - - public boolean packet() { - return this == Packet || this == Both; - } - - public boolean client() { - return this == Client || this == Both; - } - } - public enum RenderMode { Normal, Smooth, diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/KillAura.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/KillAura.java index 882843e222b..aeea4ad28f7 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/KillAura.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/KillAura.java @@ -19,6 +19,7 @@ import meteordevelopment.meteorclient.utils.player.InvUtils; import meteordevelopment.meteorclient.utils.player.PlayerUtils; import meteordevelopment.meteorclient.utils.player.Rotations; +import meteordevelopment.meteorclient.utils.player.SwingMode; import meteordevelopment.meteorclient.utils.world.TickRate; import meteordevelopment.orbit.EventHandler; import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket; @@ -461,7 +462,7 @@ private void attack(Entity target) { Rotations.rotate(Rotations.getYaw(target), Rotations.getPitch(target, Target.Body)); mc.gameMode.attack(mc.player, target); - mc.player.swing(InteractionHand.MAIN_HAND, mc.player.getMainHandItem().getAttackAnimation(), false); + SwingMode.Both.attackSwing(InteractionHand.MAIN_HAND); hitTimer = 0; } diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/Notebot.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/Notebot.java index 7bab429e464..783af50ff09 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/Notebot.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/Notebot.java @@ -28,6 +28,7 @@ import meteordevelopment.meteorclient.utils.notebot.song.Note; import meteordevelopment.meteorclient.utils.notebot.song.Song; import meteordevelopment.meteorclient.utils.player.Rotations; +import meteordevelopment.meteorclient.utils.player.SwingMode; import meteordevelopment.meteorclient.utils.render.NametagUtils; import meteordevelopment.meteorclient.utils.render.color.Color; import meteordevelopment.meteorclient.utils.render.color.SettingColor; @@ -37,6 +38,7 @@ import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket; import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.Util; import net.minecraft.world.InteractionHand; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.NoteBlock; @@ -727,16 +729,17 @@ public boolean loadFileToMap(File file, Runnable callback) { } catch (Exception e) { throw new RuntimeException(e); } - }); + }, Util.nonCriticalIoPool()); loadingSongFuture.completeOnTimeout(null, 60, TimeUnit.SECONDS); stage = Stage.LoadingSong; long time1 = System.currentTimeMillis(); - loadingSongFuture.whenComplete((song, ex) -> { + loadingSongFuture.whenCompleteAsync((song, ex) -> { + String baseName = FilenameUtils.getBaseName(file.getName()); if (ex == null) { // Song is null only when it times out if (song == null) { - error("Loading song '" + FilenameUtils.getBaseName(file.getName()) + "' timed out."); + error("Loading song '" + baseName + "' timed out."); onSongEnd(); return; } @@ -745,18 +748,18 @@ public boolean loadFileToMap(File file, Runnable callback) { long time2 = System.currentTimeMillis(); long diff = time2 - time1; - info("Song '" + FilenameUtils.getBaseName(file.getName()) + "' has been loaded to the memory! Took " + diff + "ms"); + info("Song '" + baseName + "' has been loaded to the memory! Took " + diff + "ms"); callback.run(); } else { if (ex instanceof CancellationException) { - error("Loading song '" + FilenameUtils.getBaseName(file.getName()) + "' was cancelled."); + error("Loading song '" + baseName + "' was cancelled."); } else { - error("An error occurred while loading song '" + FilenameUtils.getBaseName(file.getName()) + "'. See the logs for more details"); - MeteorClient.LOG.error("An error occurred while loading song '{}'", FilenameUtils.getBaseName(file.getName()), ex); + error("An error occurred while loading song '" + baseName + "'. See the logs for more details"); + MeteorClient.LOG.error("An error occurred while loading song '{}'", baseName, ex); onSongEnd(); } } - }); + }, mc); return true; } @@ -834,7 +837,7 @@ private void tuneBlocks() { } if (swingArm.get()) { - mc.player.swing(InteractionHand.MAIN_HAND, mc.player.getMainHandItem().getAttackAnimation(), false); + SwingMode.Both.interactSwing(InteractionHand.MAIN_HAND); } int iterations = 0; @@ -904,7 +907,7 @@ private void onTickPlay() { // Swing arm if (swingArm.get()) { - mc.player.swing(InteractionHand.MAIN_HAND, mc.player.getMainHandItem().getAttackAnimation(), false); + SwingMode.Both.attackSwing(InteractionHand.MAIN_HAND); } // Play notes diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/AntiAFK.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/AntiAFK.java index 7340e540089..00e481a8e0e 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/AntiAFK.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/AntiAFK.java @@ -12,6 +12,7 @@ import meteordevelopment.meteorclient.utils.Utils; import meteordevelopment.meteorclient.utils.player.ChatUtils; import meteordevelopment.meteorclient.utils.player.Rotations; +import meteordevelopment.meteorclient.utils.player.SwingMode; import meteordevelopment.orbit.EventHandler; import java.util.List; @@ -185,7 +186,7 @@ private void onTick(TickEvent.Pre event) { // Swing if (swing.get() && random.nextInt(99) == 0) { - mc.player.swing(mc.player.getUsedItemHand(), mc.player.getItemInHand(mc.player.getUsedItemHand()).getInteractAnimation(), false); + SwingMode.Both.attackSwing(mc.player.getUsedItemHand()); } // Sneak diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/GhostHand.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/GhostHand.java index 17eb07c87bb..50fa732c28d 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/GhostHand.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/GhostHand.java @@ -9,6 +9,7 @@ import meteordevelopment.meteorclient.events.entity.player.DoItemUseEvent; import meteordevelopment.meteorclient.systems.modules.Categories; import meteordevelopment.meteorclient.systems.modules.Module; +import meteordevelopment.meteorclient.utils.player.SwingMode; import meteordevelopment.orbit.EventHandler; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -42,14 +43,16 @@ private void onTick(DoItemUseEvent event) { for (int i = 1; i < mc.player.blockInteractionRange() * 10; i++) { BlockPos pos = BlockPos.containing(mc.player.getEyePosition(mc.getDeltaTracker().getGameTimeDeltaPartialTick(true)).add(direction.scale(i))); - if (posList.contains(pos)) continue; - posList.add(pos); + if (!posList.add(pos)) continue; if (mc.level.getBlockState(pos).hasBlockEntity()) { for (InteractionHand hand : InteractionHand.values()) { InteractionResult result = mc.gameMode.useItemOn(mc.player, hand, new BlockHitResult(new Vec3(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5), Direction.UP, pos, true)); + if (result instanceof InteractionResult.Success success && success.swingSource() == InteractionResult.SwingSource.PREDICTED) { + SwingMode.Both.interactSwing(hand); + } + if (result instanceof InteractionResult.Success || result instanceof InteractionResult.Fail) { - mc.player.swing(hand, mc.player.getItemInHand(hand).getInteractAnimation(), false); event.cancel(); return; } diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/world/HighwayBuilder.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/world/HighwayBuilder.java index e770f8a1aa9..70e8493c359 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/world/HighwayBuilder.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/world/HighwayBuilder.java @@ -762,7 +762,7 @@ private void tickDoubleMine() { normalMining.stopDestroying(); } - mc.player.swing(InteractionHand.MAIN_HAND, mc.player.getMainHandItem().getInteractAnimation(), false); + SwingMode.Both.attackSwing(InteractionHand.MAIN_HAND); } if (packetMining != null) { diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/world/Nuker.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/world/Nuker.java index 4473c4d6872..65bfa42b261 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/world/Nuker.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/world/Nuker.java @@ -22,6 +22,7 @@ import meteordevelopment.meteorclient.utils.misc.input.KeyAction; import meteordevelopment.meteorclient.utils.player.PlayerUtils; import meteordevelopment.meteorclient.utils.player.Rotations; +import meteordevelopment.meteorclient.utils.player.SwingMode; import meteordevelopment.meteorclient.utils.render.RenderUtils; import meteordevelopment.meteorclient.utils.render.color.SettingColor; import meteordevelopment.meteorclient.utils.world.BlockIterator; @@ -31,7 +32,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; -import net.minecraft.network.protocol.game.ServerboundPunchPacket; import net.minecraft.world.InteractionHand; import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.block.Block; @@ -518,8 +518,7 @@ private void breakBlock(BlockPos blockPos) { // Packet mine mode mc.gameMode.startPrediction(mc.level, sequence -> new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK, blockPos, BlockUtils.getDirection(blockPos), sequence)); - if (swing.get()) mc.player.swing(InteractionHand.MAIN_HAND, mc.player.getMainHandItem().getAttackAnimation(), false); - else mc.getConnection().send(new ServerboundPunchPacket()); + SwingMode.of(swing.get()).attackSwing(InteractionHand.MAIN_HAND); mc.gameMode.startPrediction(mc.level, sequence -> new ServerboundPlayerActionPacket(ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK, blockPos, BlockUtils.getDirection(blockPos), sequence)); } else { diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/world/VeinMiner.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/world/VeinMiner.java index 9a99e4e32e2..91392b09416 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/world/VeinMiner.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/world/VeinMiner.java @@ -16,6 +16,7 @@ import meteordevelopment.meteorclient.utils.misc.ListMode; import meteordevelopment.meteorclient.utils.misc.Pool; import meteordevelopment.meteorclient.utils.player.Rotations; +import meteordevelopment.meteorclient.utils.player.SwingMode; import meteordevelopment.meteorclient.utils.render.color.SettingColor; import meteordevelopment.meteorclient.utils.world.BlockUtils; import meteordevelopment.orbit.EventHandler; @@ -221,7 +222,7 @@ public boolean shouldRemove() { public void mine() { if (!mining) { - mc.player.swing(InteractionHand.MAIN_HAND, mc.player.getMainHandItem().getAttackAnimation(), false); + SwingMode.of(swingHand.get()).attackSwing(InteractionHand.MAIN_HAND); mining = true; } if (rotate.get()) diff --git a/src/main/java/meteordevelopment/meteorclient/utils/player/SwingMode.java b/src/main/java/meteordevelopment/meteorclient/utils/player/SwingMode.java new file mode 100644 index 00000000000..49d29cec73a --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/utils/player/SwingMode.java @@ -0,0 +1,43 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.utils.player; + +import net.minecraft.network.protocol.game.ServerboundPunchPacket; +import net.minecraft.world.InteractionHand; + +import static meteordevelopment.meteorclient.MeteorClient.mc; + +public enum SwingMode { + Both, + Packet, + Client, + None; + + public static SwingMode of(boolean client) { + return client ? Client : Packet; + } + + public boolean client() { + return this == Client || this == Both; + } + + private boolean server() { + return this == Packet || this == Both; + } + + public void attackSwing(InteractionHand hand) { + if (client()) mc.player.swing(hand, mc.player.getItemInHand(hand).getAttackAnimation(), false); + if (server()) mc.getConnection().send(ServerboundPunchPacket.INSTANCE); + } + + /** + * Interaction swings are only predicted by the client, the server broadcasts them from its own + * interaction result. There is no packet to send, so {@link #Packet} and {@link #None} behave alike. + */ + public void interactSwing(InteractionHand hand) { + if (client()) mc.player.swing(hand, mc.player.getItemInHand(hand).getInteractAnimation(), false); + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/utils/world/BlockUtils.java b/src/main/java/meteordevelopment/meteorclient/utils/world/BlockUtils.java index 9b08a06b62b..a1b0563b6c6 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/world/BlockUtils.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/world/BlockUtils.java @@ -15,11 +15,11 @@ import meteordevelopment.meteorclient.utils.player.InvUtils; import meteordevelopment.meteorclient.utils.player.Rotations; import meteordevelopment.meteorclient.utils.player.SlotUtils; +import meteordevelopment.meteorclient.utils.player.SwingMode; import meteordevelopment.orbit.EventHandler; import meteordevelopment.orbit.EventPriority; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.network.protocol.game.ServerboundPunchPacket; import net.minecraft.tags.FluidTags; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -136,9 +136,8 @@ public static void interact(BlockHitResult blockHitResult, InteractionHand hand, InteractionResult result = mc.gameMode.useItemOn(mc.player, hand, blockHitResult); - if (result.consumesAction()) { - if (swing) mc.player.swing(hand, mc.player.getItemInHand(hand).getInteractAnimation(), false); - else mc.getConnection().send(new ServerboundPunchPacket()); + if (result instanceof InteractionResult.Success success && success.swingSource() == InteractionResult.SwingSource.PREDICTED) { + SwingMode.of(swing).interactSwing(hand); } mc.player.setShiftKeyDown(wasSneaking); @@ -252,8 +251,7 @@ public static boolean breakBlock(BlockPos blockPos, boolean swing) { mc.gameMode.continueDestroyBlock(pos, getDirection(blockPos)); else mc.gameMode.startDestroyBlock(pos, getDirection(blockPos)); - if (swing) mc.player.swing(InteractionHand.MAIN_HAND, mc.player.getMainHandItem().getAttackAnimation(), false); - else mc.getConnection().send(new ServerboundPunchPacket()); + SwingMode.of(swing).attackSwing(InteractionHand.MAIN_HAND); breaking = true; breakingThisTick = true;