package top.r3944realms.lib39.util; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.PlayerInfo; import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.resources.DefaultPlayerSkin; import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.ResolvableProfile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import top.r3944realms.lib39.Lib39; import top.r3944realms.lib39.util.nbt.NBTReader; import top.r3944realms.lib39.util.nbt.NBTWriter; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Collection; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; /** * The type GameProfile helper. */ public class GameProfileHelper { /** * Client Only Class */ public static class ClientOpt implements IClientOnly { /** * Resolve skin texture resource location. * * @param gameProfile the game profile * @return the resource location */ public static @NotNull ResourceLocation resolveSkinTexture(@NotNull GameProfile gameProfile) { return IClientOnly.check(() -> Minecraft.getInstance().getSkinManager() .getInsecureSkin(gameProfile)).texture(); } /** * Gets skin texture. * * @param gameProfile the game profile * @return the skin texture */ public static ResourceLocation getSkinTexture(@Nullable GameProfile gameProfile) { return IClientOnly.check(() -> { if (gameProfile == null) { return Lib39.mrl("textures/entity/player/wide/steve.png"); } return resolveSkinTexture(gameProfile); }); } /** * Has slim arms client boolean. * * @param player the player * @return the boolean */ public static boolean hasSlimArmsClient(Player player) { return IClientOnly.check(() -> { if (player instanceof AbstractClientPlayer clientPlayer) { PlayerInfo playerInfo = Objects.requireNonNull(Minecraft.getInstance() .getConnection()) .getPlayerInfo(clientPlayer.getUUID()); return playerInfo != null && "slim".equals(playerInfo.getSkin().model().id()); } return false; }); } /** * Gets skin model name. * * @param player the player * @return the skin model name */ public static @NotNull String getSkinModelName(@NotNull Player player) { return IClientOnly.check(() -> { if (player.level().isClientSide && player instanceof AbstractClientPlayer) { PlayerInfo info = Objects.requireNonNull(Minecraft.getInstance().getConnection()) .getPlayerInfo(player.getUUID()); return info != null ? info.getSkin().model().id() : "default"; } return "default"; }); } } /** * The constant TAG_OWN_PROFILE. */ public static final String TAG_OWN_PROFILE = "OwnerProfile"; /** * Gets skin texture. * * @param gameProfile the game profile * @return the skin texture */ public static ResourceLocation getSkinTexture(@Nullable GameProfile gameProfile) { return ClientOpt.getSkinTexture(gameProfile); } /** * Resolve skin texture resource location. * * @param gameProfile the game profile * @return the resource location */ public static @NotNull ResourceLocation resolveSkinTexture(@NotNull GameProfile gameProfile) { return ClientOpt.resolveSkinTexture(gameProfile); } /** * Has slim arms boolean. * * @param player the player * @return the boolean */ public static boolean hasSlimArms(@NotNull Player player) { if (player.level().isClientSide) { return hasSlimArmsClient(player); } else { return hasSlimArmsServer(player); } } private static boolean hasSlimArmsClient(Player player) { return ClientOpt.hasSlimArmsClient(player); } // 服务器端判断 private static boolean hasSlimArmsServer(@NotNull Player player) { GameProfile profile = player.getGameProfile(); for (Property property : profile.getProperties().get("textures")) { try { String json = new String(Base64.getDecoder().decode(property.value())); JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); JsonObject textures = obj.getAsJsonObject("textures"); JsonObject skin = textures.getAsJsonObject("SKIN"); if (skin.has("metadata")) { JsonObject metadata = skin.getAsJsonObject("metadata"); if (metadata.has("model")) { return "slim".equals(metadata.get("model").getAsString()); } } } catch (Exception e) { // 解析失败,使用默认 } } return false; } /** * Gets skin model name. * * @param player the player * @return the skin model name */ public static @NotNull String getSkinModelName(@NotNull Player player) { return ClientOpt.getSkinModelName(player); } /** * 判断玩家是否为纤细手臂(Alex模型) * * @param profile 玩家的GameProfile * @return true =纤细手臂,false=正常手臂 */ public static boolean isSlimArms(GameProfile profile) { if (profile == null) { return false; } // 获取textures属性 Collection textures = profile.getProperties().get("textures"); if (textures.isEmpty()) { return false; // 没有皮肤数据,使用默认 } // 获取第一个texture属性(通常是皮肤) Property textureProperty = textures.iterator().next(); String value = textureProperty.value(); try { return isSlimFromTextureData(value); } catch (Exception e) { // 解析失败,使用默认 return false; } } /** * 从Base64编码的皮肤数据判断 * @param encodedTexture Base64编码的皮肤数据 * @return true=纤细手臂 */ private static boolean isSlimFromTextureData(String encodedTexture) { if (encodedTexture == null || encodedTexture.isEmpty()) { return false; } try { // 1. Base64解码 byte[] decodedBytes = Base64.getDecoder().decode(encodedTexture); String jsonString = new String(decodedBytes, StandardCharsets.UTF_8); // 2. 解析JSON JsonObject root = JsonParser.parseString(jsonString).getAsJsonObject(); // 3. 导航到textures -> SKIN JsonObject textures = root.getAsJsonObject("textures"); if (textures == null) { return false; } JsonObject skin = textures.getAsJsonObject("SKIN"); if (skin == null) { return false; } // 4. 检查metadata -> model JsonObject metadata = skin.getAsJsonObject("metadata"); if (metadata == null) { return false; // 没有metadata,使用默认 } String model = metadata.get("model").getAsString(); return "slim".equals(model); } catch (Exception e) { // 解析过程中出现任何错误,返回默认值 return false; } } /** * 获取皮肤模型名称 * * @param profile GameProfile * @return "slim" 或 "default" */ public static String getSkinModelName(GameProfile profile) { if (profile == null) { return "default"; } Collection textures = profile.getProperties().get("textures"); if (textures.isEmpty()) { return "default"; } Property textureProperty = textures.iterator().next(); String value = textureProperty.value(); try { byte[] decodedBytes = Base64.getDecoder().decode(value); String jsonString = new String(decodedBytes, StandardCharsets.UTF_8); JsonObject root = JsonParser.parseString(jsonString).getAsJsonObject(); JsonObject texturesObj = root.getAsJsonObject("textures"); JsonObject skin = texturesObj.getAsJsonObject("SKIN"); if (skin.has("metadata")) { JsonObject metadata = skin.getAsJsonObject("metadata"); if (metadata.has("model")) { return metadata.get("model").getAsString(); } } } catch (Exception e) { // 忽略错误 } return "default"; } /** * 从ItemStack的组件中读取GameProfile * * @param stack the stack * @return the profile from item stack */ @Nullable public static GameProfile getProfileFromItemStack(ItemStack stack) { if (stack.isEmpty()) { return null; } if (stack.isEmpty()) return null; ResolvableProfile profile = stack.get(DataComponents.PROFILE); return profile != null ? profile.gameProfile() : null; } /** * 将GameProfile保存到ItemStack的组件 * * @param stack the stack * @param profile the profile */ public static void saveProfileToItemStack(@NotNull ItemStack stack, @Nullable GameProfile profile) { if (stack.isEmpty()) return; if (profile == null) { stack.remove(DataComponents.PROFILE); } else { stack.set(DataComponents.PROFILE, new ResolvableProfile(profile)); } } /** * 检查ItemStack是否有保存的皮肤数据 * * @param stack the stack * @return the boolean */ public static boolean hasProfileData(@NotNull ItemStack stack) { if (stack.isEmpty()) { return false; } return !stack.isEmpty() && stack.has(DataComponents.PROFILE); } }