所以我在过去的一年里一直在寻找改变玩家姓名标签的所有解决方案,其中有 2 个脱颖而出,一个是使用反射,另一个是使用 protocollib。
我面临的问题是我无法按照我想要的方式获得正确的名牌!
ProtocolLib 解决方案
public static void changePlayerNameTag(Player player, String newName) {
// Update nametag for other players
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(EssentialsNicks.getPlugin(), PacketType.Play.Server.PLAYER_INFO) {
@Override
public void onPacketSending(PacketEvent event) {
if (event.getPacket().getPlaye`your text`rInfoAction().read(0) != EnumWrappers.PlayerInfoAction.ADD_PLAYER) return;
List<PlayerInfoData> newPlayerInfoDataList = new ArrayList<PlayerInfoData>();
List<PlayerInfoData> playerInfoDataList = event.getPacket().getPlayerInfoDataLists().read(0);
for (PlayerInfoData playerInfoData : playerInfoDataList) {
if (playerInfoData == null || playerInfoData.getProfile() == null || Bukkit.getPlayer(playerInfoData.getProfile().getUUID()) == null) { //Unknown Player
newPlayerInfoDataList.add(playerInfoData);
continue;
}
// Check if the player's UUID matches the specific player's UUID
if (!playerInfoData.getProfile().getUUID().equals(player.getUniqueId())) {
newPlayerInfoDataList.add(playerInfoData);
continue;
}
WrappedGameProfile profile = playerInfoData.getProfile();
// Create a new profile with the desired name and the original profile's properties (including the skin)
WrappedGameProfile newProfile = new WrappedGameProfile(profile.getUUID(), ChatColor.translateAlternateColorCodes('&', newName));
newProfile.getProperties().putAll(profile.getProperties());
PlayerInfoData newPlayerInfoData = new PlayerInfoData(newProfile, playerInfoData.getLatency(), playerInfoData.getGameMode(), playerInfoData.getDisplayName());
newPlayerInfoDataList.add(newPlayerInfoData);
}
event.getPacket().getPlayerInfoDataLists().write(0, newPlayerInfoDataList);
}
});
Bukkit.getOnlinePlayers().forEach(onlinePlayer -> {
protocolManager.sendServerPacket(onlinePlayer, packet);
onlinePlayer.hidePlayer(player);
onlinePlayer.showPlayer(player);
});
}
反思
public static void changeName(String name, Player player) {
try {
Method getHandle = player.getClass().getMethod("getHandle");
Object entityPlayer = getHandle.invoke(player);
Class<?> entityHuman = entityPlayer.getClass().getSuperclass();
Field gameProfileField;
int majVersion = Integer.parseInt(Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3].replaceAll("(v|R[0-9]+)", "").split("_")[0]);
int minVersion = Integer.parseInt(Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3].replaceAll("(v|R[0-9]+)", "").split("_")[1]);
if (majVersion >= 1 && minVersion >= 9) {
gameProfileField = entityHuman.getDeclaredField("bS");
} else {
gameProfileField = entityHuman.getDeclaredField("bH");
}
gameProfileField.setAccessible(true);
gameProfileField.set(entityPlayer, new GameProfile(player.getUniqueId(), name));
for (Player players : Bukkit.getOnlinePlayers()) {
players.hidePlayer(player);
players.showPlayer(player);
}
} catch (NoSuchMethodException | SecurityException | InvocationTargetException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
每个人都有自己的问题,我试图两全其美,有点像 hypixel
常见问题 玩家自己的名牌不会改变(客户端)
ProtocolLib 的问题 更改后的姓名标签不适用于团队
反射的问题 对于Reflection,它会更改您自己的minecraft名称(显然仅在服务器上),我在Reflection上面临的问题是,它会使minecraft和所有其他插件命令不可用,而且您甚至无法使用tab键来获取您的名称或更改后的名称.
老实说,如果我能解决协议库名称标签不适用于团队的问题,我会很高兴,但我有点希望玩家自己的名称标签也能改变。
如果有人知道我不知道的事情,我请求你的帮助!
当涉及到更改玩家的实际姓名标签时,您已经发现了一些唯一的方法:通过更改协议和使用反射。然而,在大多数用例中,这些方法是不合适的,因为它们会改变其他功能的行为,例如您遇到的名称未出现在制表符补全、插件命令等中的问题。如果您更改服务器上的玩家名称,到处都会发生本质上的改变。
要实现自定义名称标签的结果,正如您在 Hypixel 等服务器上看到的那样,您可以利用一个巧妙的解决方法。您可以通过编程方式在玩家位置创建一个隐形盔甲架,并更改 盔甲架实体 的名称标签,而不是更改 player 名称,从而创建自定义玩家名称标签的错觉,而实际上您正在查看而是在隐形盔甲架的名牌上。
自己实现这一点可能非常困难,但是,我建议您使用一个库来为您处理所有盔甲架技巧。尝试利用 Neznamy 的 TAB 资源(开发者 API)。