如何在Spigot 1.21.1中刷新玩家以更换皮肤?
解决1.21.1 Spigot服务器更换玩家皮肤的问题
你的问题出在数据包发送逻辑上:仅发送移除和添加玩家列表的数据包不足以让客户端刷新皮肤,且玩家信息更新的动作类型错误,导致皮肤未被重新加载。以下是修正方案:
核心问题分析
ClientboundPlayerInfoRemovePacket会将玩家从客户端列表移除,而Action.ADD_PLAYER仅适用于添加新玩家,无法更新已有玩家的皮肤信息。- 仅修改GameProfile的纹理属性后,客户端不会自动重新渲染玩家实体,必须触发实体的重新生成逻辑。
修正后的代码
@Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) { String playerName = sender.getName(); if (!(sender.hasPermission("northmclobbyutils.duckify")) && sender instanceof Player) { sender.sendMessage("§cYou do not have permission to use this command!"); return true; } if (!(sender instanceof Player)) { playerName = "Console"; } for (Player p : sender.getServer().getOnlinePlayers()) { CraftPlayer cp = (CraftPlayer) p; ServerPlayer sp = cp.getHandle(); // 修改显示名称与GameProfile纹理 sp.setCustomName(Component.literal("§6Duck")); PropertyMap pm = sp.getGameProfile().getProperties(); pm.removeAll("textures"); pm.put("textures", new Property("textures", "ewogICJ0aW1lc3RhbXAiIDogMTczMTIxMzc0NTk1NywKICAicHJvZmlsZUlkIiA6ICI1OWJlOTUyY2EyNjc0OWE4OTM1YmQ5MGQxNmMyN2M4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJUaGVQZW5zcGlubmV1ciIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS80MTlkNTQ3NTk3YTQ2NmM0MWY1YmI3YjE0Y2MyMTZmMTk2ZDNhZjI0YjJhYWQ3MWZjMjlkNWE4ZTlkY2RiZmY5IgogICAgfQogIH0KfQ==", "D8GnL/k1kpQCuals0OqfQSGIqdUZA3akLfSH2UZpwIDfXGn6A3BQmMzpaMw63JxITiZOWruMkzbUtbLreP421kzvNKikkgmXVDIOpyLLc+WYfT+O2eZLAMKnf4/f/cWlnl05FDXeJqIpRyx9b463Uh9xam2zuqr5rHq9t0+/1DOnfew/sDEkJWjNUkUooaJBV+raAHTYmnWlEXCItmfkNJtfBVYN+OVE9w7aSJpC8rkC3bAtkdKFxMmzTuN93pIZBqUzVMwf68e7jAdGMaWiDKl6wptDm304dl2iwuDJeYWDdmKD01KdHZIva1HI/DiPEUTSoPaR7GF055mOp14/08kAkHJA63hQyH/6RE9J1YOolMjho9qlznZbSmjU1yIM7X8vPNuLDcCh0S1OjbraNFrtruzi1vY8nxViPMRVuulUWDtFftF3esQ52M4CsGmeZ8VP786zFc88c+SrezzALgI9TrFklQKl/oZZud8WfjCUnFal2MYdZSqXaMWoYw9QBss1AJABqaLFglVwRckcRVna48J35uFm9kI4aWZVx6WYOKgxqlSKQ1EbKnvVSSfxbZZeHS3u9+2jsIBFpishaFpOtdOKuGM0X6Dx8e+Q4ur4cmT5UV5Q1rLFJhEzxC+Turo2luu3xc63vGn9hUYjj2pCn5DM9vwHKnz6Ef49o1Q=")); // 1. 发送玩家信息更新包,同步Profile(含皮肤)与显示名称 sp.connection.send(new ClientboundPlayerInfoUpdatePacket( EnumSet.of(Action.UPDATE_PROFILE, Action.UPDATE_DISPLAY_NAME), sp )); // 2. 移除并重新生成玩家实体,触发客户端加载新皮肤 sp.connection.send(new ClientboundRemoveEntitiesPacket(sp.getId())); sp.connection.send(new ClientboundAddPlayerPacket(sp)); // 3. 发送位置与旋转包,避免玩家实体位置偏移 sp.connection.send(new ClientboundMoveEntityPosPacket(sp.getId(), (short) (sp.getX() * 4096), (short) (sp.getY() * 4096), (short) (sp.getZ() * 4096), true )); sp.connection.send(new ClientboundMoveEntityRotPacket(sp.getId(), (byte) ((sp.getYRot() * 256) / 360), (byte) ((sp.getXRot() * 256) / 360), true )); } sender.sendMessage("§aAll players have been duckified!"); return true; }
关键修正点
- 使用
EnumSet.of(Action.UPDATE_PROFILE, Action.UPDATE_DISPLAY_NAME)作为更新动作,让客户端同步玩家的GameProfile(含皮肤纹理)和显示名称,替代原有的移除后重新添加逻辑。 - 发送
ClientboundRemoveEntitiesPacket和ClientboundAddPlayerPacket:强制客户端先移除原实体,再加载更新后的实体,确保新皮肤被读取。 - 补充位置与旋转数据包,防止玩家在客户端出现位置偏移问题。
额外注意事项
- 若保留
updatePlayerSkin方法,需确保它不会覆盖或重置GameProfile的纹理属性。 - 玩家自身视角的皮肤更新可能需要额外发送
ClientboundRespawnPacket,但上述代码已满足其他玩家看到更新后皮肤的需求。
内容的提问来源于stack exchange,提问作者Polarbaer007
相关产品推荐
相关产品推荐

