SkinManager.java

net.minecraft.client.resources.SkinManager

信息

  • 全限定名:net.minecraft.client.resources.SkinManager
  • 类型:public class
  • 包:net.minecraft.client.resources
  • 源码路径:src/main/java/net/minecraft/client/resources/SkinManager.java
  • 起始行号:L38
  • 职责:

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L39
    • 说明:

      TODO

  • services

    • 类型: Services
    • 修饰符: private final
    • 源码定位: L40
    • 说明:

      TODO

  • skinTextureDownloader

    • 类型: SkinTextureDownloader
    • 修饰符: private final
    • 源码定位: L41
    • 说明:

      TODO

  • skinCache

    • 类型: LoadingCache<SkinManager.CacheKey,CompletableFuture<Optional<PlayerSkin>>>
    • 修饰符: private final
    • 源码定位: L42
    • 说明:

      TODO

  • skinTextures

    • 类型: SkinManager.TextureCache
    • 修饰符: private final
    • 源码定位: L43
    • 说明:

      TODO

  • capeTextures

    • 类型: SkinManager.TextureCache
    • 修饰符: private final
    • 源码定位: L44
    • 说明:

      TODO

  • elytraTextures

    • 类型: SkinManager.TextureCache
    • 修饰符: private final
    • 源码定位: L45
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.resources.SkinManager.CacheKey

    • 类型: record
    • 修饰符: private
    • 源码定位: L142
    • 说明:

      TODO

  • net.minecraft.client.resources.SkinManager.TextureCache

    • 类型: class
    • 修饰符: private
    • 源码定位: L146
    • 说明:

      TODO

构造器

public SkinManager(Path skinsDirectory, Services services, SkinTextureDownloader skinTextureDownloader, Executor mainThreadExecutor) @ L47

  • 构造器名:SkinManager
  • 源码定位:L47
  • 修饰符:public

参数:

  • skinsDirectory: Path
  • services: Services
  • skinTextureDownloader: SkinTextureDownloader
  • mainThreadExecutor: Executor

说明:

TODO

方法

下面的方法块按源码顺序生成。

public Supplier<PlayerSkin> createLookup(GameProfile profile, boolean requireSecure) @ L88

  • 方法名:createLookup
  • 源码定位:L88
  • 返回类型:Supplier
  • 修饰符:public

参数:

  • profile: GameProfile
  • requireSecure: boolean

说明:

TODO

public CompletableFuture<Optional<PlayerSkin>> get(GameProfile profile) @ L104

  • 方法名:get
  • 源码定位:L104
  • 返回类型:CompletableFuture<Optional>
  • 修饰符:public

参数:

  • profile: GameProfile

说明:

TODO

private CompletableFuture<PlayerSkin> registerTextures(UUID profileId, MinecraftProfileTextures textures) @ L114

  • 方法名:registerTextures
  • 源码定位:L114
  • 返回类型:CompletableFuture
  • 修饰符:private

参数:

  • profileId: UUID
  • textures: MinecraftProfileTextures

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class SkinManager {
    private static final Logger LOGGER = LogUtils.getLogger();
    private final Services services;
    private final SkinTextureDownloader skinTextureDownloader;
    private final LoadingCache<SkinManager.CacheKey, CompletableFuture<Optional<PlayerSkin>>> skinCache;
    private final SkinManager.TextureCache skinTextures;
    private final SkinManager.TextureCache capeTextures;
    private final SkinManager.TextureCache elytraTextures;
 
    public SkinManager(Path skinsDirectory, Services services, SkinTextureDownloader skinTextureDownloader, Executor mainThreadExecutor) {
        this.services = services;
        this.skinTextureDownloader = skinTextureDownloader;
        this.skinTextures = new SkinManager.TextureCache(skinsDirectory, Type.SKIN);
        this.capeTextures = new SkinManager.TextureCache(skinsDirectory, Type.CAPE);
        this.elytraTextures = new SkinManager.TextureCache(skinsDirectory, Type.ELYTRA);
        this.skinCache = CacheBuilder.newBuilder()
            .expireAfterAccess(Duration.ofSeconds(15L))
            .build(
                new CacheLoader<SkinManager.CacheKey, CompletableFuture<Optional<PlayerSkin>>>() {
                    {
                        Objects.requireNonNull(SkinManager.this);
                    }
 
                    public CompletableFuture<Optional<PlayerSkin>> load(SkinManager.CacheKey key) {
                        return CompletableFuture.<MinecraftProfileTextures>supplyAsync(() -> {
                                Property packedTextures = key.packedTextures();
                                if (packedTextures == null) {
                                    return MinecraftProfileTextures.EMPTY;
                                } else {
                                    MinecraftProfileTextures textures = services.sessionService().unpackTextures(packedTextures);
                                    if (textures.signatureState() == SignatureState.INVALID) {
                                        SkinManager.LOGGER.warn("Profile contained invalid signature for textures property (profile id: {})", key.profileId());
                                    }
 
                                    return textures;
                                }
                            }, Util.backgroundExecutor().forName("unpackSkinTextures"))
                            .thenComposeAsync(textures -> SkinManager.this.registerTextures(key.profileId(), textures), mainThreadExecutor)
                            .handle((playerSkin, throwable) -> {
                                if (throwable != null) {
                                    SkinManager.LOGGER.warn("Failed to load texture for profile {}", key.profileId, throwable);
                                }
 
                                return Optional.ofNullable(playerSkin);
                            });
                    }
                }
            );
    }
 
    public Supplier<PlayerSkin> createLookup(GameProfile profile, boolean requireSecure) {
        CompletableFuture<Optional<PlayerSkin>> future = this.get(profile);
        PlayerSkin defaultSkin = DefaultPlayerSkin.get(profile);
        if (SharedConstants.DEBUG_DEFAULT_SKIN_OVERRIDE) {
            return () -> defaultSkin;
        } else {
            Optional<PlayerSkin> currentValue = future.getNow(null);
            if (currentValue != null) {
                PlayerSkin playerSkin = currentValue.filter(skin -> !requireSecure || skin.secure()).orElse(defaultSkin);
                return () -> playerSkin;
            } else {
                return () -> future.getNow(Optional.empty()).filter(skin -> !requireSecure || skin.secure()).orElse(defaultSkin);
            }
        }
    }
 
    public CompletableFuture<Optional<PlayerSkin>> get(GameProfile profile) {
        if (SharedConstants.DEBUG_DEFAULT_SKIN_OVERRIDE) {
            PlayerSkin defaultSkin = DefaultPlayerSkin.get(profile);
            return CompletableFuture.completedFuture(Optional.of(defaultSkin));
        } else {
            Property packedTextures = this.services.sessionService().getPackedTextures(profile);
            return this.skinCache.getUnchecked(new SkinManager.CacheKey(profile.id(), packedTextures));
        }
    }
 
    private CompletableFuture<PlayerSkin> registerTextures(UUID profileId, MinecraftProfileTextures textures) {
        MinecraftProfileTexture skinInfo = textures.skin();
        CompletableFuture<ClientAsset.Texture> skinTexture;
        PlayerModelType model;
        if (skinInfo != null) {
            skinTexture = this.skinTextures.getOrLoad(skinInfo);
            model = PlayerModelType.byLegacyServicesName(skinInfo.getMetadata("model"));
        } else {
            PlayerSkin defaultSkin = DefaultPlayerSkin.get(profileId);
            skinTexture = CompletableFuture.completedFuture(defaultSkin.body());
            model = defaultSkin.model();
        }
 
        MinecraftProfileTexture capeInfo = textures.cape();
        CompletableFuture<ClientAsset.Texture> capeTexture = capeInfo != null ? this.capeTextures.getOrLoad(capeInfo) : CompletableFuture.completedFuture(null);
        MinecraftProfileTexture elytraInfo = textures.elytra();
        CompletableFuture<ClientAsset.Texture> elytraTexture = elytraInfo != null
            ? this.elytraTextures.getOrLoad(elytraInfo)
            : CompletableFuture.completedFuture(null);
        return CompletableFuture.allOf(skinTexture, capeTexture, elytraTexture)
            .thenApply(
                unused -> new PlayerSkin(
                    skinTexture.join(), capeTexture.join(), elytraTexture.join(), model, textures.signatureState() == SignatureState.SIGNED
                )
            );
    }
 
    @OnlyIn(Dist.CLIENT)
    private record CacheKey(UUID profileId, @Nullable Property packedTextures) {
    }
 
    @OnlyIn(Dist.CLIENT)
    private class TextureCache {
        private final Path root;
        private final Type type;
        private final Map<String, CompletableFuture<ClientAsset.Texture>> textures;
 
        private TextureCache(Path root, Type type) {
            Objects.requireNonNull(SkinManager.this);
            super();
            this.textures = new Object2ObjectOpenHashMap<>();
            this.root = root;
            this.type = type;
        }
 
        public CompletableFuture<ClientAsset.Texture> getOrLoad(MinecraftProfileTexture texture) {
            String hash = texture.getHash();
            CompletableFuture<ClientAsset.Texture> future = this.textures.get(hash);
            if (future == null) {
                future = this.registerTexture(texture);
                this.textures.put(hash, future);
            }
 
            return future;
        }
 
        private CompletableFuture<ClientAsset.Texture> registerTexture(MinecraftProfileTexture textureInfo) {
            String hash = Hashing.sha1().hashUnencodedChars(textureInfo.getHash()).toString();
            Identifier textureId = this.getTextureLocation(hash);
            Path file = this.root.resolve(hash.length() > 2 ? hash.substring(0, 2) : "xx").resolve(hash);
            return SkinManager.this.skinTextureDownloader.downloadAndRegisterSkin(textureId, file, textureInfo.getUrl(), this.type == Type.SKIN);
        }
 
        private Identifier getTextureLocation(String textureHash) {
            String root = switch (this.type) {
                case SKIN -> "skins";
                case CAPE -> "capes";
                case ELYTRA -> "elytra";
            };
            return Identifier.withDefaultNamespace(root + "/" + textureHash);
        }
    }
}

引用的其他类

  • SkinTextureDownloader

    • 引用位置: 参数/字段
  • DefaultPlayerSkin

    • 引用位置: 方法调用
    • 关联成员: DefaultPlayerSkin.get()
  • Identifier

    • 引用位置: 方法调用
    • 关联成员: Identifier.withDefaultNamespace()
  • Services

    • 引用位置: 参数/字段
  • Util

    • 引用位置: 方法调用
    • 关联成员: Util.backgroundExecutor()
  • PlayerModelType

    • 引用位置: 方法调用
    • 关联成员: PlayerModelType.byLegacyServicesName()
  • PlayerSkin

    • 引用位置: 字段/构造调用/返回值
    • 关联成员: PlayerSkin()