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.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.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; 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 { public static final String TAG_BE = "BlockEntityTag"; public static final String TAG_OWN_PROFILE = "OwnerProfile"; /** * Gets skin texture. * * @param gameProfile the game profile * @return the skin texture */ @OnlyIn(Dist.CLIENT) public static ResourceLocation getSkinTexture(@Nullable GameProfile gameProfile) { if (gameProfile == null) { return DefaultPlayerSkin.getDefaultSkin(); } return resolveSkinTexture(gameProfile); } /** * Resolve skin texture resource location. * * @param gameProfile the game profile * @return the resource location */ @OnlyIn(Dist.CLIENT) public static @NotNull ResourceLocation resolveSkinTexture(@NotNull GameProfile gameProfile) { Minecraft minecraft = Minecraft.getInstance(); return minecraft.getSkinManager() .getInsecureSkinLocation(gameProfile); } // 判断玩家是否使用纤细手臂 public static boolean hasSlimArms(@NotNull Player player) { if (player.level().isClientSide) { return hasSlimArmsClient(player); } else { return hasSlimArmsServer(player); } } // 客户端判断 @OnlyIn(Dist.CLIENT) private static boolean hasSlimArmsClient(Player player) { if (player instanceof AbstractClientPlayer clientPlayer) { PlayerInfo playerInfo = Objects.requireNonNull(Minecraft.getInstance() .getConnection()) .getPlayerInfo(clientPlayer.getUUID()); return playerInfo != null && "slim".equals(playerInfo.getModelName()); } return false; } // 服务器端判断 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.getValue())); 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; } // 获取皮肤模型名称 @OnlyIn(Dist.CLIENT) public static @NotNull String getSkinModelName(@NotNull Player player) { if (player.level().isClientSide && player instanceof AbstractClientPlayer) { PlayerInfo info = Objects.requireNonNull(Minecraft.getInstance().getConnection()) .getPlayerInfo(player.getUUID()); return info != null ? info.getModelName() : "default"; } return "default"; } /** * 判断玩家是否为纤细手臂(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.getValue(); 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.getValue(); 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的NBT中读取GameProfile */ @Nullable public static GameProfile getProfileFromItemStack(ItemStack stack) { if (stack.isEmpty()) { return null; } CompoundTag tag = stack.getTag(); if (tag == null) { return null; } AtomicReference profileRef = new AtomicReference<>(); // 检查方块实体数据 NBTReader.of(tag) .compound(TAG_BE, compoundTag -> NBTReader.of(compoundTag) .compound("OwnerProfile", ct -> profileRef.set(NbtUtils.readGameProfile(ct))) ) .compound("OwnerProfile", ct -> { if (profileRef.get() == null) { //兼容写法 profileRef.set(NbtUtils.readGameProfile(ct)); } }); return profileRef.get(); } /** * 将GameProfile保存到ItemStack的NBT */ public static void saveProfileToItemStack(@NotNull ItemStack stack, @Nullable GameProfile profile) { if (stack.isEmpty()) { return; } CompoundTag tag = stack.getOrCreateTag(); if (profile == null) { // 移除现有数据 NBTReader.of(tag) .compound(TAG_BE, ct -> tag.remove(TAG_OWN_PROFILE)); tag.remove(TAG_BE); tag.remove(TAG_OWN_PROFILE); return; } // 创建方块实体数据 NBTWriter.of(tag) .compound(TAG_BE, writer -> writer .compound(TAG_OWN_PROFILE, NbtUtils.writeGameProfile(new CompoundTag(), profile)) ); } /** * 检查ItemStack是否有保存的皮肤数据 */ public static boolean hasProfileData(@NotNull ItemStack stack) { if (stack.isEmpty()) { return false; } CompoundTag tag = stack.getTag(); if (tag == null) { return false; } if (tag.contains(TAG_BE)) { CompoundTag blockEntityTag = tag.getCompound(TAG_BE); return blockEntityTag.contains(TAG_OWN_PROFILE); } return tag.contains(TAG_OWN_PROFILE); } }