You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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异常修复

报错核心原因:

  1. 使用了错误的PlayerInfoAction:ADD_PLAYER用于添加新玩家到列表,而非更新已有玩家的显示名称,应改用UPDATE_DISPLAY_NAME。
  2. 数据包仅发给目标玩家自己,但Nametag是给其他玩家看的,需要发给所有在线玩家。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 18:35:54