AccountProfileKeyPairManager.java

net.minecraft.client.multiplayer.AccountProfileKeyPairManager

信息

  • 全限定名:net.minecraft.client.multiplayer.AccountProfileKeyPairManager
  • 类型:public class
  • 包:net.minecraft.client.multiplayer
  • 源码路径:src/main/java/net/minecraft/client/multiplayer/AccountProfileKeyPairManager.java
  • 起始行号:L36
  • 实现:ProfileKeyPairManager
  • 职责:

    TODO

字段/常量

  • LOGGER

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

      TODO

  • MINIMUM_PROFILE_KEY_REFRESH_INTERVAL

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

      TODO

  • PROFILE_KEY_PAIR_DIR

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

      TODO

  • userApiService

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

      TODO

  • profileKeyPairPath

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

      TODO

  • keyPair

    • 类型: CompletableFuture<Optional<ProfileKeyPair>>
    • 修饰符: private
    • 源码定位: L42
    • 说明:

      TODO

  • nextProfileKeyRefreshTime

    • 类型: Instant
    • 修饰符: private
    • 源码定位: L43
    • 说明:

      TODO

内部类/嵌套类型

构造器

public AccountProfileKeyPairManager(UserApiService userApiService, UUID profileId, Path gameDirectory) @ L45

  • 构造器名:AccountProfileKeyPairManager
  • 源码定位:L45
  • 修饰符:public

参数:

  • userApiService: UserApiService
  • profileId: UUID
  • gameDirectory: Path

说明:

TODO

方法

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

public CompletableFuture<Optional<ProfileKeyPair>> prepareKeyPair() @ L50

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

参数:

说明:

TODO

public boolean shouldRefreshKeyPair() @ L57

  • 方法名:shouldRefreshKeyPair
  • 源码定位:L57
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

private CompletableFuture<Optional<ProfileKeyPair>> readOrFetchProfileKeyPair(Optional<ProfileKeyPair> cachedKeyPair) @ L64

  • 方法名:readOrFetchProfileKeyPair
  • 源码定位:L64
  • 返回类型:CompletableFuture<Optional>
  • 修饰符:private

参数:

  • cachedKeyPair: Optional

说明:

TODO

private Optional<ProfileKeyPair> readProfileKeyPair() @ L86

  • 方法名:readProfileKeyPair
  • 源码定位:L86
  • 返回类型:Optional
  • 修饰符:private

参数:

说明:

TODO

private void writeProfileKeyPair(ProfileKeyPair profileKeyPair) @ L104

  • 方法名:writeProfileKeyPair
  • 源码定位:L104
  • 返回类型:void
  • 修饰符:private

参数:

  • profileKeyPair: ProfileKeyPair

说明:

TODO

private ProfileKeyPair fetchProfileKeyPair(UserApiService userApiService) @ L125

  • 方法名:fetchProfileKeyPair
  • 源码定位:L125
  • 返回类型:ProfileKeyPair
  • 修饰符:private

参数:

  • userApiService: UserApiService

说明:

TODO

private static ProfilePublicKey.Data parsePublicKey(KeyPairResponse response) @ L137

  • 方法名:parsePublicKey
  • 源码定位:L137
  • 返回类型:ProfilePublicKey.Data
  • 修饰符:private static

参数:

  • response: KeyPairResponse

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class AccountProfileKeyPairManager implements ProfileKeyPairManager {
    private static final Logger LOGGER = LogUtils.getLogger();
    private static final Duration MINIMUM_PROFILE_KEY_REFRESH_INTERVAL = Duration.ofHours(1L);
    private static final Path PROFILE_KEY_PAIR_DIR = Path.of("profilekeys");
    private final UserApiService userApiService;
    private final Path profileKeyPairPath;
    private CompletableFuture<Optional<ProfileKeyPair>> keyPair = CompletableFuture.completedFuture(Optional.empty());
    private Instant nextProfileKeyRefreshTime = Instant.EPOCH;
 
    public AccountProfileKeyPairManager(UserApiService userApiService, UUID profileId, Path gameDirectory) {
        this.userApiService = userApiService;
        this.profileKeyPairPath = gameDirectory.resolve(PROFILE_KEY_PAIR_DIR).resolve(profileId + ".json");
    }
 
    @Override
    public CompletableFuture<Optional<ProfileKeyPair>> prepareKeyPair() {
        this.nextProfileKeyRefreshTime = Instant.now().plus(MINIMUM_PROFILE_KEY_REFRESH_INTERVAL);
        this.keyPair = this.keyPair.thenCompose(this::readOrFetchProfileKeyPair);
        return this.keyPair;
    }
 
    @Override
    public boolean shouldRefreshKeyPair() {
        return this.keyPair.isDone() && Instant.now().isAfter(this.nextProfileKeyRefreshTime)
            ? this.keyPair.join().map(ProfileKeyPair::dueRefresh).orElse(true)
            : false;
    }
 
    private CompletableFuture<Optional<ProfileKeyPair>> readOrFetchProfileKeyPair(Optional<ProfileKeyPair> cachedKeyPair) {
        return CompletableFuture.supplyAsync(() -> {
            if (cachedKeyPair.isPresent() && !cachedKeyPair.get().dueRefresh()) {
                if (!SharedConstants.IS_RUNNING_IN_IDE) {
                    this.writeProfileKeyPair(null);
                }
 
                return cachedKeyPair;
            } else {
                try {
                    ProfileKeyPair fetchedKeyPair = this.fetchProfileKeyPair(this.userApiService);
                    this.writeProfileKeyPair(fetchedKeyPair);
                    return Optional.ofNullable(fetchedKeyPair);
                } catch (CryptException | MinecraftClientException | IOException var3) {
                    LOGGER.error("Failed to retrieve profile key pair", (Throwable)var3);
                    this.writeProfileKeyPair(null);
                    return cachedKeyPair;
                }
            }
        }, Util.nonCriticalIoPool());
    }
 
    private Optional<ProfileKeyPair> readProfileKeyPair() {
        if (Files.notExists(this.profileKeyPairPath)) {
            return Optional.empty();
        } else {
            try {
                Optional var2;
                try (BufferedReader bufferedReader = Files.newBufferedReader(this.profileKeyPairPath)) {
                    var2 = ProfileKeyPair.CODEC.parse(JsonOps.INSTANCE, StrictJsonParser.parse(bufferedReader)).result();
                }
 
                return var2;
            } catch (Exception var6) {
                LOGGER.error("Failed to read profile key pair file {}", this.profileKeyPairPath, var6);
                return Optional.empty();
            }
        }
    }
 
    private void writeProfileKeyPair(@Nullable ProfileKeyPair profileKeyPair) {
        try {
            Files.deleteIfExists(this.profileKeyPairPath);
        } catch (IOException var3) {
            LOGGER.error("Failed to delete profile key pair file {}", this.profileKeyPairPath, var3);
        }
 
        if (profileKeyPair != null) {
            if (SharedConstants.IS_RUNNING_IN_IDE) {
                ProfileKeyPair.CODEC.encodeStart(JsonOps.INSTANCE, profileKeyPair).ifSuccess(jsonStr -> {
                    try {
                        Files.createDirectories(this.profileKeyPairPath.getParent());
                        Files.writeString(this.profileKeyPairPath, jsonStr.toString());
                    } catch (Exception var3x) {
                        LOGGER.error("Failed to write profile key pair file {}", this.profileKeyPairPath, var3x);
                    }
                });
            }
        }
    }
 
    private @Nullable ProfileKeyPair fetchProfileKeyPair(UserApiService userApiService) throws CryptException, IOException {
        KeyPairResponse keyPair = userApiService.getKeyPair();
        if (keyPair != null) {
            ProfilePublicKey.Data publicKeyData = parsePublicKey(keyPair);
            return new ProfileKeyPair(
                Crypt.stringToPemRsaPrivateKey(keyPair.keyPair().privateKey()), new ProfilePublicKey(publicKeyData), Instant.parse(keyPair.refreshedAfter())
            );
        } else {
            return null;
        }
    }
 
    private static ProfilePublicKey.Data parsePublicKey(KeyPairResponse response) throws CryptException {
        KeyPair keyPair = response.keyPair();
        if (keyPair != null
            && !Strings.isNullOrEmpty(keyPair.publicKey())
            && response.publicKeySignature() != null
            && response.publicKeySignature().array().length != 0) {
            try {
                Instant expiresAt = Instant.parse(response.expiresAt());
                PublicKey key = Crypt.stringToRsaPublicKey(keyPair.publicKey());
                ByteBuffer signature = response.publicKeySignature();
                return new ProfilePublicKey.Data(expiresAt, key, signature.array());
            } catch (IllegalArgumentException | DateTimeException var5) {
                throw new CryptException(var5);
            }
        } else {
            throw new CryptException(new MissingException("Missing public key"));
        }
    }
}

引用的其他类

  • ProfileKeyPairManager

    • 引用位置: 实现
  • Crypt

    • 引用位置: 方法调用
    • 关联成员: Crypt.stringToPemRsaPrivateKey(), Crypt.stringToRsaPublicKey()
  • CryptException

    • 引用位置: 构造调用
    • 关联成员: CryptException()
  • StrictJsonParser

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

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

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

    • 引用位置: 方法调用/构造调用/返回值
    • 关联成员: Data(), ProfilePublicKey(), ProfilePublicKey.Data()