342 lines
11 KiB
Java
342 lines
11 KiB
Java
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<Property> 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<Property> 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);
|
||
}
|
||
|
||
}
|
||
|