Minecraft Bukkit插件:手持物品时取消左键挥臂动画失败
问题:手持指定物品时取消左键手臂挥动动画
原实现代码
@EventHandler public void left(PlayerAnimationEvent event) { Player plr = event.getPlayer(); ItemStack hand = plr.getInventory().getItemInMainHand(); if (event.getAnimationType() == PlayerAnimationType.ARM_SWING) { if (hand.getType() == Material.DIAMOND) { event.setCancelled(true); getLogger().info(plr.getName() + " diamond "); } else { getLogger().info(plr.getName() + " not diamond " + hand.getType()); } } }
服务器日志输出
[03:53:33] [Server thread/INFO]: [EE2] FN7182 not diamond AIR [03:53:33] [Server thread/INFO]: [EE2] FN7182 not diamond AIR [03:53:34] [Server thread/INFO]: [EE2] FN7182 not diamond IRON_SWORD [03:53:35] [Server thread/INFO]: [EE2] FN7182 not diamond IRON_SWORD [03:53:36] [Server thread/INFO]: [EE2] FN7182 diamond [03:53:36] [Server thread/INFO]: [EE2] FN7182 diamond
问题原因
PlayerAnimationEvent的setCancelled(true)仅能阻止服务端将手臂挥动的动画同步给其他玩家,无法取消触发动画的玩家自己客户端上的动画播放——因为客户端是本地触发动画后才通知服务端,此时动画已经在玩家屏幕上显示了。
解决方案
1. 仅阻止其他玩家看到动画(原代码优化)
原代码其实已经实现了这个效果,如果你只需要其他玩家看不到目标玩家的挥动手臂动画,无需修改代码,只是玩家自己仍会看到动画。
2. 拦截左键点击触发的动画(覆盖点击场景)
如果你的需求是玩家点击左键(空气/方块)时取消动画,可以监听PlayerInteractEvent,在事件触发时直接取消,从源头阻止动画触发:
@EventHandler public void onLeftClickInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); ItemStack mainHand = player.getInventory().getItemInMainHand(); // 检测是否是左键点击动作 Action action = event.getAction(); if ((action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK) && mainHand.getType() == Material.DIAMOND) { // 取消点击事件,阻止动画触发 event.setCancelled(true); getLogger().info(player.getName() + " diamond "); } else { getLogger().info(player.getName() + " not diamond " + mainHand.getType()); } }
这种方式可以阻止玩家点击左键时的动画,但无法处理玩家单纯挥动手臂(未点击)的场景。
3. 全场景阻止动画(需依赖ProtocolLib)
如果要覆盖所有挥动手臂的场景(包括单纯挥臂),需要使用ProtocolLib拦截客户端发送的动画数据包,同时阻止服务端同步:
import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketEvent; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; // 在插件启用时注册数据包监听器 public void onEnable() { ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(this, ListenerPriority.NORMAL, PacketType.Play.Client.ANIMATION) { @Override public void onPacketReceiving(PacketEvent event) { Player player = event.getPlayer(); if (player.getInventory().getItemInMainHand().getType() == Material.DIAMOND) { // 拦截客户端发送的挥臂数据包,服务端不会处理,也不会同步给其他玩家 event.setCancelled(true); } } }); }
注意:这种方式仍无法完全阻止玩家自己客户端的动画(因为客户端本地播放动画不依赖服务端响应),但可以确保其他玩家看不到动画,同时服务端不会处理该动画事件。
补充说明
纯服务端插件无法完全取消玩家自己客户端上的手臂挥动动画,因为该动画是客户端本地触发的。如果需要让玩家自己也看不到动画,必须借助客户端mod或资源包修改动画逻辑。
内容的提问来源于stack exchange,提问作者Async
相关产品推荐
相关产品推荐

