DownloadedPackSource.java
net.minecraft.client.resources.server.DownloadedPackSource
信息
- 全限定名:net.minecraft.client.resources.server.DownloadedPackSource
- 类型:public class
- 包:net.minecraft.client.resources.server
- 源码路径:src/main/java/net/minecraft/client/resources/server/DownloadedPackSource.java
- 起始行号:L52
- 实现:AutoCloseable
- 职责:
TODO
字段/常量
-
SERVER_NAME- 类型:
Component - 修饰符:
private static final - 源码定位:
L53 - 说明:
TODO
- 类型:
-
SHA1- 类型:
Pattern - 修饰符:
private static final - 源码定位:
L54 - 说明:
TODO
- 类型:
-
LOGGER- 类型:
Logger - 修饰符:
private static final - 源码定位:
L55 - 说明:
TODO
- 类型:
-
EMPTY_SOURCE- 类型:
RepositorySource - 修饰符:
private static final - 源码定位:
L56 - 说明:
TODO
- 类型:
-
DOWNLOADED_PACK_SELECTION- 类型:
PackSelectionConfig - 修饰符:
private static final - 源码定位:
L57 - 说明:
TODO
- 类型:
-
LOG_ONLY_FEEDBACK- 类型:
PackLoadFeedback - 修饰符:
private static final public public - 源码定位:
L58 - 说明:
TODO
- 类型:
-
minecraft- 类型:
Minecraft - 修饰符:
private final - 源码定位:
L69 - 说明:
TODO
- 类型:
-
packSource- 类型:
RepositorySource - 修饰符:
private - 源码定位:
L70 - 说明:
TODO
- 类型:
-
pendingReload- 类型:
PackReloadConfig.Callbacks - 修饰符:
private - 源码定位:
L71 - 说明:
TODO
- 类型:
-
manager- 类型:
ServerPackManager - 修饰符:
private final - 源码定位:
L72 - 说明:
TODO
- 类型:
-
downloadQueue- 类型:
DownloadQueue - 修饰符:
private final - 源码定位:
L73 - 说明:
TODO
- 类型:
-
packType- 类型:
PackSource - 修饰符:
private - 源码定位:
L74 - 说明:
TODO
- 类型:
-
packFeedback- 类型:
PackLoadFeedback - 修饰符:
private - 源码定位:
L75 - 说明:
TODO
- 类型:
-
packIdSerialNumber- 类型:
int - 修饰符:
private - 源码定位:
L76 - 说明:
TODO
- 类型:
内部类/嵌套类型
- 无
构造器
public DownloadedPackSource(Minecraft minecraft, Path packCache, GameConfig.UserData user) @ L78
- 构造器名:DownloadedPackSource
- 源码定位:L78
- 修饰符:public
参数:
- minecraft: Minecraft
- packCache: Path
- user: GameConfig.UserData
说明:
TODO
方法
下面的方法块按源码顺序生成。
private HttpUtil.DownloadProgressListener createDownloadNotifier(int totalCount) @ L105
- 方法名:createDownloadNotifier
- 源码定位:L105
- 返回类型:HttpUtil.DownloadProgressListener
- 修饰符:private
参数:
- totalCount: int
说明:
TODO
private PackDownloader createDownloader(DownloadQueue downloadQueue, Executor mainThreadExecutor, User user, Proxy proxy) @ L180
- 方法名:createDownloader
- 源码定位:L180
- 返回类型:PackDownloader
- 修饰符:private
参数:
- downloadQueue: DownloadQueue
- mainThreadExecutor: Executor
- user: User
- proxy: Proxy
说明:
TODO
private Runnable createUpdateScheduler(Executor mainThreadExecutor) @ L224
- 方法名:createUpdateScheduler
- 源码定位:L224
- 返回类型:Runnable
- 修饰符:private
参数:
- mainThreadExecutor: Executor
说明:
TODO
private PackReloadConfig createReloadConfig() @ L253
- 方法名:createReloadConfig
- 源码定位:L253
- 返回类型:PackReloadConfig
- 修饰符:private
参数:
- 无
说明:
TODO
private List<Pack> loadRequestedPacks(List<PackReloadConfig.IdAndPath> packsToLoad) @ L257
- 方法名:loadRequestedPacks
- 源码定位:L257
- 返回类型:List
- 修饰符:private
参数:
- packsToLoad: List<PackReloadConfig.IdAndPath>
说明:
TODO
public RepositorySource createRepositorySource() @ L278
- 方法名:createRepositorySource
- 源码定位:L278
- 返回类型:RepositorySource
- 修饰符:public
参数:
- 无
说明:
TODO
private static RepositorySource configureSource(List<Pack> packs) @ L282
- 方法名:configureSource
- 源码定位:L282
- 返回类型:RepositorySource
- 修饰符:private static
参数:
- packs: List
说明:
TODO
private void startReload(PackReloadConfig.Callbacks callbacks) @ L286
- 方法名:startReload
- 源码定位:L286
- 返回类型:void
- 修饰符:private
参数:
- callbacks: PackReloadConfig.Callbacks
说明:
TODO
public void onRecovery() @ L304
- 方法名:onRecovery
- 源码定位:L304
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public void onRecoveryFailure() @ L317
- 方法名:onRecoveryFailure
- 源码定位:L317
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public void onReloadSuccess() @ L325
- 方法名:onReloadSuccess
- 源码定位:L325
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
private static HashCode tryParseSha1Hash(String hash) @ L332
- 方法名:tryParseSha1Hash
- 源码定位:L332
- 返回类型:HashCode
- 修饰符:private static
参数:
- hash: String
说明:
TODO
public void pushPack(UUID id, URL url, String hash) @ L336
- 方法名:pushPack
- 源码定位:L336
- 返回类型:void
- 修饰符:public
参数:
- id: UUID
- url: URL
- hash: String
说明:
TODO
public void pushLocalPack(UUID id, Path path) @ L341
- 方法名:pushLocalPack
- 源码定位:L341
- 返回类型:void
- 修饰符:public
参数:
- id: UUID
- path: Path
说明:
TODO
public void popPack(UUID id) @ L345
- 方法名:popPack
- 源码定位:L345
- 返回类型:void
- 修饰符:public
参数:
- id: UUID
说明:
TODO
public void popAll() @ L349
- 方法名:popAll
- 源码定位:L349
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
private static PackLoadFeedback createPackResponseSender(Connection connection) @ L353
- 方法名:createPackResponseSender
- 源码定位:L353
- 返回类型:PackLoadFeedback
- 修饰符:private static
参数:
- connection: Connection
说明:
TODO
public void configureForServerControl(Connection connection, ServerPackManager.PackPromptStatus packPromptStatus) @ L382
- 方法名:configureForServerControl
- 源码定位:L382
- 返回类型:void
- 修饰符:public
参数:
- connection: Connection
- packPromptStatus: ServerPackManager.PackPromptStatus
说明:
TODO
public void configureForLocalWorld() @ L397
- 方法名:configureForLocalWorld
- 源码定位:L397
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public void allowServerPacks() @ L403
- 方法名:allowServerPacks
- 源码定位:L403
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public void rejectServerPacks() @ L407
- 方法名:rejectServerPacks
- 源码定位:L407
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public CompletableFuture<Void> waitForPackFeedback(UUID packId) @ L411
- 方法名:waitForPackFeedback
- 源码定位:L411
- 返回类型:CompletableFuture
- 修饰符:public
参数:
- packId: UUID
说明:
TODO
public void cleanupAfterDisconnect() @ L441
- 方法名:cleanupAfterDisconnect
- 源码定位:L441
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public void close() @ L447
- 方法名:close
- 源码定位:L447
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
代码
@OnlyIn(Dist.CLIENT)
public class DownloadedPackSource implements AutoCloseable {
private static final Component SERVER_NAME = Component.translatable("resourcePack.server.name");
private static final Pattern SHA1 = Pattern.compile("^[a-fA-F0-9]{40}$");
private static final Logger LOGGER = LogUtils.getLogger();
private static final RepositorySource EMPTY_SOURCE = result -> {};
private static final PackSelectionConfig DOWNLOADED_PACK_SELECTION = new PackSelectionConfig(true, Pack.Position.TOP, true);
private static final PackLoadFeedback LOG_ONLY_FEEDBACK = new PackLoadFeedback() {
@Override
public void reportUpdate(UUID id, PackLoadFeedback.Update update) {
DownloadedPackSource.LOGGER.debug("Downloaded pack {} changed state to {}", id, update);
}
@Override
public void reportFinalResult(UUID id, PackLoadFeedback.FinalResult result) {
DownloadedPackSource.LOGGER.debug("Downloaded pack {} finished with state {}", id, result);
}
};
private final Minecraft minecraft;
private RepositorySource packSource = EMPTY_SOURCE;
private PackReloadConfig.@Nullable Callbacks pendingReload;
private final ServerPackManager manager;
private final DownloadQueue downloadQueue;
private PackSource packType = PackSource.SERVER;
private PackLoadFeedback packFeedback = LOG_ONLY_FEEDBACK;
private int packIdSerialNumber;
public DownloadedPackSource(Minecraft minecraft, Path packCache, GameConfig.UserData user) {
this.minecraft = minecraft;
try {
this.downloadQueue = new DownloadQueue(packCache);
} catch (IOException var5) {
throw new UncheckedIOException("Failed to open download queue in directory " + packCache, var5);
}
Executor executor = minecraft::schedule;
this.manager = new ServerPackManager(this.createDownloader(this.downloadQueue, executor, user.user, user.proxy), new PackLoadFeedback() {
{
Objects.requireNonNull(DownloadedPackSource.this);
}
@Override
public void reportUpdate(UUID id, PackLoadFeedback.Update result) {
DownloadedPackSource.this.packFeedback.reportUpdate(id, result);
}
@Override
public void reportFinalResult(UUID id, PackLoadFeedback.FinalResult result) {
DownloadedPackSource.this.packFeedback.reportFinalResult(id, result);
}
}, this.createReloadConfig(), this.createUpdateScheduler(executor), ServerPackManager.PackPromptStatus.PENDING);
}
private HttpUtil.DownloadProgressListener createDownloadNotifier(int totalCount) {
return new HttpUtil.DownloadProgressListener() {
private final SystemToast.SystemToastId toastId;
private Component title;
private @Nullable Component message;
private int count;
private int failCount;
private OptionalLong totalBytes;
{
Objects.requireNonNull(DownloadedPackSource.this);
this.toastId = new SystemToast.SystemToastId();
this.title = Component.empty();
this.message = null;
this.totalBytes = OptionalLong.empty();
}
private void updateToast() {
DownloadedPackSource.this.minecraft
.execute(() -> SystemToast.addOrUpdate(DownloadedPackSource.this.minecraft.getToastManager(), this.toastId, this.title, this.message));
}
private void updateProgress(long bytesSoFar) {
if (this.totalBytes.isPresent()) {
this.message = Component.translatable("download.pack.progress.percent", bytesSoFar * 100L / this.totalBytes.getAsLong());
} else {
this.message = Component.translatable("download.pack.progress.bytes", Unit.humanReadable(bytesSoFar));
}
this.updateToast();
}
@Override
public void requestStart() {
this.count++;
this.title = Component.translatable("download.pack.title", this.count, totalCount);
this.updateToast();
DownloadedPackSource.LOGGER.debug("Starting pack {}/{} download", this.count, totalCount);
}
@Override
public void downloadStart(OptionalLong sizeBytes) {
DownloadedPackSource.LOGGER.debug("File size = {} bytes", sizeBytes);
this.totalBytes = sizeBytes;
this.updateProgress(0L);
}
@Override
public void downloadedBytes(long bytesSoFar) {
DownloadedPackSource.LOGGER.debug("Progress for pack {}: {} bytes", this.count, bytesSoFar);
this.updateProgress(bytesSoFar);
}
@Override
public void requestFinished(boolean success) {
if (!success) {
DownloadedPackSource.LOGGER.info("Pack {} failed to download", this.count);
this.failCount++;
} else {
DownloadedPackSource.LOGGER.debug("Download ended for pack {}", this.count);
}
if (this.count == totalCount) {
if (this.failCount > 0) {
this.title = Component.translatable("download.pack.failed", this.failCount, totalCount);
this.message = null;
this.updateToast();
} else {
SystemToast.forceHide(DownloadedPackSource.this.minecraft.getToastManager(), this.toastId);
}
}
}
};
}
private PackDownloader createDownloader(DownloadQueue downloadQueue, Executor mainThreadExecutor, User user, Proxy proxy) {
return new PackDownloader() {
private static final int MAX_PACK_SIZE_BYTES = 262144000;
private static final HashFunction CACHE_HASHING_FUNCTION = Hashing.sha1();
{
Objects.requireNonNull(DownloadedPackSource.this);
}
private Map<String, String> createDownloadHeaders() {
WorldVersion version = SharedConstants.getCurrentVersion();
return Map.of(
"X-Minecraft-Username",
user.getName(),
"X-Minecraft-UUID",
UndashedUuid.toString(user.getProfileId()),
"X-Minecraft-Version",
version.name(),
"X-Minecraft-Version-ID",
version.id(),
"X-Minecraft-Pack-Format",
String.valueOf(version.packVersion(PackType.CLIENT_RESOURCES)),
"User-Agent",
"Minecraft Java/" + version.name()
);
}
@Override
public void download(Map<UUID, DownloadQueue.DownloadRequest> requests, Consumer<DownloadQueue.BatchResult> output) {
downloadQueue.downloadBatch(
new DownloadQueue.BatchConfig(
CACHE_HASHING_FUNCTION,
262144000,
this.createDownloadHeaders(),
proxy,
DownloadedPackSource.this.createDownloadNotifier(requests.size())
),
requests
)
.thenAcceptAsync(output, mainThreadExecutor);
}
};
}
private Runnable createUpdateScheduler(Executor mainThreadExecutor) {
return new Runnable() {
private boolean scheduledInMainExecutor;
private boolean hasUpdates;
{
Objects.requireNonNull(DownloadedPackSource.this);
}
@Override
public void run() {
this.hasUpdates = true;
if (!this.scheduledInMainExecutor) {
this.scheduledInMainExecutor = true;
mainThreadExecutor.execute(this::runAllUpdates);
}
}
private void runAllUpdates() {
while (this.hasUpdates) {
this.hasUpdates = false;
DownloadedPackSource.this.manager.tick();
}
this.scheduledInMainExecutor = false;
}
};
}
private PackReloadConfig createReloadConfig() {
return this::startReload;
}
private @Nullable List<Pack> loadRequestedPacks(List<PackReloadConfig.IdAndPath> packsToLoad) {
List<Pack> packs = new ArrayList<>(packsToLoad.size());
for (PackReloadConfig.IdAndPath idAndPath : Lists.reverse(packsToLoad)) {
String name = String.format(Locale.ROOT, "server/%08X/%s", this.packIdSerialNumber++, idAndPath.id());
Path path = idAndPath.path();
PackLocationInfo packLocationInfo = new PackLocationInfo(name, SERVER_NAME, this.packType, Optional.empty());
Pack.ResourcesSupplier resources = new FilePackResources.FileResourcesSupplier(path);
PackFormat currentPackVersion = SharedConstants.getCurrentVersion().packVersion(PackType.CLIENT_RESOURCES);
Pack.Metadata metadata = Pack.readPackMetadata(packLocationInfo, resources, currentPackVersion, PackType.CLIENT_RESOURCES);
if (metadata == null) {
LOGGER.warn("Invalid pack metadata in {}, ignoring all", path);
return null;
}
packs.add(new Pack(packLocationInfo, resources, metadata, DOWNLOADED_PACK_SELECTION));
}
return packs;
}
public RepositorySource createRepositorySource() {
return output -> this.packSource.loadPacks(output);
}
private static RepositorySource configureSource(List<Pack> packs) {
return packs.isEmpty() ? EMPTY_SOURCE : packs::forEach;
}
private void startReload(PackReloadConfig.Callbacks callbacks) {
this.pendingReload = callbacks;
List<PackReloadConfig.IdAndPath> normalPacks = callbacks.packsToLoad();
List<Pack> packs = this.loadRequestedPacks(normalPacks);
if (packs == null) {
callbacks.onFailure(false);
List<PackReloadConfig.IdAndPath> recoveryPacks = callbacks.packsToLoad();
packs = this.loadRequestedPacks(recoveryPacks);
if (packs == null) {
LOGGER.warn("Double failure in loading server packs");
packs = List.of();
}
}
this.packSource = configureSource(packs);
this.minecraft.reloadResourcePacks();
}
public void onRecovery() {
if (this.pendingReload != null) {
this.pendingReload.onFailure(false);
List<Pack> packs = this.loadRequestedPacks(this.pendingReload.packsToLoad());
if (packs == null) {
LOGGER.warn("Double failure in loading server packs");
packs = List.of();
}
this.packSource = configureSource(packs);
}
}
public void onRecoveryFailure() {
if (this.pendingReload != null) {
this.pendingReload.onFailure(true);
this.pendingReload = null;
this.packSource = EMPTY_SOURCE;
}
}
public void onReloadSuccess() {
if (this.pendingReload != null) {
this.pendingReload.onSuccess();
this.pendingReload = null;
}
}
private static @Nullable HashCode tryParseSha1Hash(@Nullable String hash) {
return hash != null && SHA1.matcher(hash).matches() ? HashCode.fromString(hash.toLowerCase(Locale.ROOT)) : null;
}
public void pushPack(UUID id, URL url, @Nullable String hash) {
HashCode parsedHash = tryParseSha1Hash(hash);
this.manager.pushPack(id, url, parsedHash);
}
public void pushLocalPack(UUID id, Path path) {
this.manager.pushLocalPack(id, path);
}
public void popPack(UUID id) {
this.manager.popPack(id);
}
public void popAll() {
this.manager.popAll();
}
private static PackLoadFeedback createPackResponseSender(Connection connection) {
return new PackLoadFeedback() {
@Override
public void reportUpdate(UUID id, PackLoadFeedback.Update result) {
DownloadedPackSource.LOGGER.debug("Pack {} changed status to {}", id, result);
ServerboundResourcePackPacket.Action response = switch (result) {
case ACCEPTED -> ServerboundResourcePackPacket.Action.ACCEPTED;
case DOWNLOADED -> ServerboundResourcePackPacket.Action.DOWNLOADED;
};
connection.send(new ServerboundResourcePackPacket(id, response));
}
@Override
public void reportFinalResult(UUID id, PackLoadFeedback.FinalResult result) {
DownloadedPackSource.LOGGER.debug("Pack {} changed status to {}", id, result);
ServerboundResourcePackPacket.Action response = switch (result) {
case APPLIED -> ServerboundResourcePackPacket.Action.SUCCESSFULLY_LOADED;
case DOWNLOAD_FAILED -> ServerboundResourcePackPacket.Action.FAILED_DOWNLOAD;
case DECLINED -> ServerboundResourcePackPacket.Action.DECLINED;
case DISCARDED -> ServerboundResourcePackPacket.Action.DISCARDED;
case ACTIVATION_FAILED -> ServerboundResourcePackPacket.Action.FAILED_RELOAD;
};
connection.send(new ServerboundResourcePackPacket(id, response));
}
};
}
public void configureForServerControl(Connection connection, ServerPackManager.PackPromptStatus packPromptStatus) {
this.packType = PackSource.SERVER;
this.packFeedback = createPackResponseSender(connection);
switch (packPromptStatus) {
case ALLOWED:
this.manager.allowServerPacks();
break;
case DECLINED:
this.manager.rejectServerPacks();
break;
case PENDING:
this.manager.resetPromptStatus();
}
}
public void configureForLocalWorld() {
this.packType = PackSource.WORLD;
this.packFeedback = LOG_ONLY_FEEDBACK;
this.manager.allowServerPacks();
}
public void allowServerPacks() {
this.manager.allowServerPacks();
}
public void rejectServerPacks() {
this.manager.rejectServerPacks();
}
public CompletableFuture<Void> waitForPackFeedback(UUID packId) {
final CompletableFuture<Void> result = new CompletableFuture<>();
final PackLoadFeedback original = this.packFeedback;
this.packFeedback = new PackLoadFeedback() {
{
Objects.requireNonNull(DownloadedPackSource.this);
}
@Override
public void reportUpdate(UUID id, PackLoadFeedback.Update result) {
original.reportUpdate(id, result);
}
@Override
public void reportFinalResult(UUID id, PackLoadFeedback.FinalResult status) {
if (packId.equals(id)) {
DownloadedPackSource.this.packFeedback = original;
if (status == PackLoadFeedback.FinalResult.APPLIED) {
result.complete(null);
} else {
result.completeExceptionally(new IllegalStateException("Failed to apply pack " + id + ", reason: " + status));
}
}
original.reportFinalResult(id, status);
}
};
return result;
}
public void cleanupAfterDisconnect() {
this.manager.popAll();
this.packFeedback = LOG_ONLY_FEEDBACK;
this.manager.resetPromptStatus();
}
@Override
public void close() throws IOException {
this.downloadQueue.close();
}
}引用的其他类
-
- 引用位置:
方法调用 - 关联成员:
Unit.humanReadable()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SharedConstants.getCurrentVersion()
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用/构造调用 - 关联成员:
SystemToast.SystemToastId(), SystemToast.addOrUpdate(), SystemToast.forceHide(), SystemToastId()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
构造调用/返回值 - 关联成员:
PackDownloader()
- 引用位置:
-
- 引用位置:
字段/构造调用/返回值 - 关联成员:
PackLoadFeedback()
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
参数/字段/构造调用 - 关联成员:
ServerPackManager()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
字段/方法调用 - 关联成员:
Component.empty(), Component.translatable()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ServerboundResourcePackPacket()
- 引用位置:
-
- 引用位置:
参数/字段/方法调用/构造调用 - 关联成员:
BatchConfig(), DownloadQueue(), DownloadQueue.BatchConfig()
- 引用位置:
-
- 引用位置:
方法调用/构造调用 - 关联成员:
FilePackResources.FileResourcesSupplier(), FileResourcesSupplier()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
PackLocationInfo()
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
PackSelectionConfig()
- 引用位置:
-
- 引用位置:
参数/方法调用/构造调用/返回值 - 关联成员:
Pack(), Pack.readPackMetadata()
- 引用位置:
-
- 引用位置:
字段
- 引用位置:
-
- 引用位置:
字段/返回值
- 引用位置:
-
- 引用位置:
方法调用/构造调用/返回值 - 关联成员:
DownloadProgressListener(), HttpUtil.DownloadProgressListener()
- 引用位置:
-
- 引用位置:
字段/方法调用 - 关联成员:
Pattern.compile()
- 引用位置: