Lib39/src/main/java/top/r3944realms/lib39/util/GameProfileHelper.java
3944Realms 97a232944b 更新内容
1. 修复玩偶在服务器导致崩溃的错误
2026-01-03 00:34:24 +08:00

304 lines
10 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}