使用ProtocolLib修改MC玩家Nametag时遭遇FieldAccessException问题求助
玩家Nametag多格式修改方案及ProtocolLib异常解决方法
我正在开发一款Minecraft插件,需求是玩家使用下界之星(Nether Star)后,其Nametag变为紫色加粗状态,持续2分钟后恢复正常。尝试Spigot Teams API仅能设置单一颜色,无法实现格式叠加;改用ProtocolLib修改数据包时触发FieldAccessException错误,相关代码及报错信息如下:
问题代码
package me.games.weapon; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.wrappers.WrappedChatComponent; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; import com.comphenix.protocol.wrappers.EnumWrappers; import com.comphenix.protocol.wrappers.PlayerInfoData; import com.comphenix.protocol.wrappers.WrappedGameProfile; import java.util.Collection; import java.util.Collections; public class SuperCharge implements Listener, CommandExecutor { private final JavaPlugin plugin; private final ProtocolManager protocolManager; public SuperCharge(JavaPlugin plugin) { this.plugin = plugin; this.protocolManager = ProtocolLibrary.getProtocolManager(); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (sender instanceof Player) { Player player = (Player) sender; // Create a new ItemStack for the Nether Star ItemStack supercharge = new ItemStack(Material.NETHER_STAR); // Give the player the Nether Star player.getInventory().addItem(supercharge); return true; } else { sender.sendMessage("You must be a player to use this command."); return false; } } @EventHandler public void onPlayerUse(PlayerInteractEvent event) { Player player = event.getPlayer(); ItemStack item = event.getItem(); if (item != null && item.getType() == Material.NETHER_STAR) { // Apply the glowing effect for 2 minutes player.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, 2 * 60 * 20, 1)); // Change the player's name tag to purple and bold for 2 minutes changePlayerNameTag(player); // Schedule task to reset the player's name tag after 2 minutes (2400 ticks) new BukkitRunnable() { @Override public void run() { resetPlayerNameTag(player); } }.runTaskLater(plugin, 2 * 60 * 20); // Remove the Nether Star from the player's inventory player.getInventory().removeItem(new ItemStack(Material.NETHER_STAR, 1)); } } private void changePlayerNameTag(Player player) { // Currently isn't working and isn't coloring and bolding PacketContainer nameTagPacket = protocolManager.createPacket(PacketType.Play.Server.PLAYER_INFO); nameTagPacket.getPlayerInfoAction().write(0, EnumWrappers.PlayerInfoAction.ADD_PLAYER); nameTagPacket.getPlayerInfoDataLists().write(0, Collections.singletonList(new PlayerInfoData( WrappedGameProfile.fromPlayer(player), 1, EnumWrappers.NativeGameMode.fromBukkit(player.getGameMode()), WrappedChatComponent.fromText(ChatColor.BOLD + "" + ChatColor.LIGHT_PURPLE + player.getName()) ))); try { protocolManager.sendServerPacket(player, nameTagPacket); } catch (Exception e) { e.printStackTrace(); } } private void resetPlayerNameTag(Player player) { PacketContainer nameTagPacket = protocolManager.createPacket(PacketType.Play.Server.PLAYER_INFO); nameTagPacket.getPlayerInfoAction().write(0, EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME); nameTagPacket.getPlayerInfoDataLists().write(0, Collections.singletonList(new PlayerInfoData( WrappedGameProfile.fromPlayer(player), 1, EnumWrappers.NativeGameMode.fromBukkit(player.getGameMode()), WrappedChatComponent.fromText(player.getName()) // Original name without formatting ))); try { protocolManager.sendServerPacket(player, nameTagPacket); } catch (Exception e) { e.printStackTrace(); } } }
报错信息
[ERROR]: Could not pass event PlayerInteractEvent to Weapon v1.0-SNAPSHOT com.comphenix.protocol.reflect.FieldAccessException: Field index 0 is out of bounds for length 0 at com.comphenix.protocol.reflect.FieldAccessException.fromFormat(FieldAccessException.java:49) at com.comphenix.protocol.reflect.StructureModifier.write(StructureModifier.java:315) at me.iplaygames.boostweapon.SuperCharge.changePlayerNameTag(SuperCharge.java:80) at me.iplaygames.boostweapon.SuperCharge.onPlayerUse(SuperCharge.java:60) ...
解决方案
一、ProtocolLib异常修复
报错核心原因:
- 使用了错误的
PlayerInfoAction:ADD_PLAYER用于添加新玩家到列表,而非更新已有玩家的显示名称,应改用UPDATE_DISPLAY_NAME。 - 数据包仅发给目标玩家自己,但Nametag是给其他玩家看的,需要发给所有在线玩家。
PlayerInfoData中的ping值填固定1不符合规范,应使用玩家真实ping值。
修改后的代码:
private void changePlayerNameTag(Player player) { PacketContainer packet = protocolManager.createPacket(PacketType.Play.Server.PLAYER_INFO); packet.getPlayerInfoAction().write(0, EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME); WrappedChatComponent formattedName = WrappedChatComponent.fromText(ChatColor.BOLD + "" + ChatColor.LIGHT_PURPLE + player.getName()); PlayerInfoData data = new PlayerInfoData( WrappedGameProfile.fromPlayer(player), player.getPing(), EnumWrappers.NativeGameMode.fromBukkit(player.getGameMode()), formattedName ); packet.getPlayerInfoDataLists().write(0, Collections.singletonList(data)); // 发给所有在线玩家,确保所有人都能看到修改后的Nametag for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { try { protocolManager.sendServerPacket(onlinePlayer, packet); } catch (Exception e) { e.printStackTrace(); } } } private void resetPlayerNameTag(Player player) { PacketContainer packet = protocolManager.createPacket(PacketType.Play.Server.PLAYER_INFO); packet.getPlayerInfoAction().write(0, EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME); WrappedChatComponent originalName = WrappedChatComponent.fromText(player.getName()); PlayerInfoData data = new PlayerInfoData( WrappedGameProfile.fromPlayer(player), player.getPing(), EnumWrappers.NativeGameMode.fromBukkit(player.getGameMode()), originalName ); packet.getPlayerInfoDataLists().write(0, Collections.singletonList(data)); for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { try { protocolManager.sendServerPacket(onlinePlayer, packet); } catch (Exception e) { e.printStackTrace(); } } }
同时修正PlayerInteractEvent触发条件,避免左键误触消耗物品:
@EventHandler public void onPlayerUse(PlayerInteractEvent event) { Player player = event.getPlayer(); ItemStack item = event.getItem(); // 仅响应右键使用物品的动作 if (item != null && item.getType() == Material.NETHER_STAR && event.getAction().isRightClick()) { player.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, 2 * 60 * 20, 1)); changePlayerNameTag(player); new BukkitRunnable() { @Override public void run() { resetPlayerNameTag(player); } }.runTaskLater(plugin, 2 * 60 * 20); player.getInventory().removeItem(new ItemStack(Material.NETHER_STAR, 1)); } }
二、更稳定的Scoreboard Teams方案(推荐)
Spigot Teams API本身支持完整的格式叠加,之前的使用方式有误。通过创建临时队伍设置前缀/后缀,可实现任意格式的Nametag修改,且无需依赖ProtocolLib,兼容性更强。
示例代码:
private void setupNametagWithTeams(Player player) { // 创建唯一的临时队伍名称,避免冲突 String teamName = "supercharge_" + player.getUniqueId(); Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); Team team = scoreboard.getTeam(teamName); if (team == null) { team = scoreboard.registerNewTeam(teamName); } // 设置前缀为加粗紫色,后缀为空(可添加斜体、下划线等其他格式) team.setPrefix(ChatColor.BOLD + "" + ChatColor.LIGHT_PURPLE); team.setSuffix(""); // 保持队伍默认交互设置,不影响玩家PVP和隐身可见性 team.setAllowFriendlyFire(true); team.setCanSeeFriendlyInvisibles(true); // 将玩家加入队伍 if (!team.hasPlayer(player)) { team.addPlayer(player); } // 2分钟后移除玩家并清理空队伍 new BukkitRunnable() { @Override public void run() { team.removePlayer(player); if (team.getPlayers().isEmpty()) { team.unregister(); } } }.runTaskLater(plugin, 2 * 60 * 20); }
在onPlayerUse方法中替换changePlayerNameTag(player)为setupNametagWithTeams(player)即可,无需再编写resetPlayerNameTag方法,任务会自动完成清理。
内容的提问来源于stack exchange,提问作者gamer boi
相关产品推荐
相关产品推荐

