304 lines
10 KiB
Java
304 lines
10 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.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<Property> 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<Property> 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<GameProfile> 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);
|
||
}
|
||
|
||
}
|
||
|