package net.minecraft.client.resources.server; import com.google.common.hash.HashCode; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.server.packs.DownloadQueue; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public class ServerPackManager { private final PackDownloader downloader; final PackLoadFeedback packLoadFeedback; private final PackReloadConfig reloadConfig; private final Runnable updateRequest; private ServerPackManager.PackPromptStatus packPromptStatus; final List packs = new ArrayList(); public ServerPackManager( PackDownloader downloader, PackLoadFeedback packLoadFeedback, PackReloadConfig reloadConfig, Runnable updateRequest, ServerPackManager.PackPromptStatus packPromptStatus ) { this.downloader = downloader; this.packLoadFeedback = packLoadFeedback; this.reloadConfig = reloadConfig; this.updateRequest = updateRequest; this.packPromptStatus = packPromptStatus; } void registerForUpdate() { this.updateRequest.run(); } private void markExistingPacksAsRemoved(UUID id) { for (ServerPackManager.ServerPackData serverPackData : this.packs) { if (serverPackData.id.equals(id)) { serverPackData.setRemovalReasonIfNotSet(ServerPackManager.RemovalReason.SERVER_REPLACED); } } } public void pushPack(UUID id, URL url, @Nullable HashCode hash) { if (this.packPromptStatus == ServerPackManager.PackPromptStatus.DECLINED) { this.packLoadFeedback.reportFinalResult(id, PackLoadFeedback.FinalResult.DECLINED); } else { this.pushNewPack(id, new ServerPackManager.ServerPackData(id, url, hash)); } } public void pushLocalPack(UUID id, Path path) { if (this.packPromptStatus == ServerPackManager.PackPromptStatus.DECLINED) { this.packLoadFeedback.reportFinalResult(id, PackLoadFeedback.FinalResult.DECLINED); } else { URL uRL; try { uRL = path.toUri().toURL(); } catch (MalformedURLException var5) { throw new IllegalStateException("Can't convert path to URL " + path, var5); } ServerPackManager.ServerPackData serverPackData = new ServerPackManager.ServerPackData(id, uRL, null); serverPackData.downloadStatus = ServerPackManager.PackDownloadStatus.DONE; serverPackData.path = path; this.pushNewPack(id, serverPackData); } } private void pushNewPack(UUID id, ServerPackManager.ServerPackData packData) { this.markExistingPacksAsRemoved(id); this.packs.add(packData); if (this.packPromptStatus == ServerPackManager.PackPromptStatus.ALLOWED) { this.acceptPack(packData); } this.registerForUpdate(); } private void acceptPack(ServerPackManager.ServerPackData packData) { this.packLoadFeedback.reportUpdate(packData.id, PackLoadFeedback.Update.ACCEPTED); packData.promptAccepted = true; } @Nullable private ServerPackManager.ServerPackData findPackInfo(UUID id) { for (ServerPackManager.ServerPackData serverPackData : this.packs) { if (!serverPackData.isRemoved() && serverPackData.id.equals(id)) { return serverPackData; } } return null; } public void popPack(UUID id) { ServerPackManager.ServerPackData serverPackData = this.findPackInfo(id); if (serverPackData != null) { serverPackData.setRemovalReasonIfNotSet(ServerPackManager.RemovalReason.SERVER_REMOVED); this.registerForUpdate(); } } public void popAll() { for (ServerPackManager.ServerPackData serverPackData : this.packs) { serverPackData.setRemovalReasonIfNotSet(ServerPackManager.RemovalReason.SERVER_REMOVED); } this.registerForUpdate(); } public void allowServerPacks() { this.packPromptStatus = ServerPackManager.PackPromptStatus.ALLOWED; for (ServerPackManager.ServerPackData serverPackData : this.packs) { if (!serverPackData.promptAccepted && !serverPackData.isRemoved()) { this.acceptPack(serverPackData); } } this.registerForUpdate(); } public void rejectServerPacks() { this.packPromptStatus = ServerPackManager.PackPromptStatus.DECLINED; for (ServerPackManager.ServerPackData serverPackData : this.packs) { if (!serverPackData.promptAccepted) { serverPackData.setRemovalReasonIfNotSet(ServerPackManager.RemovalReason.DECLINED); } } this.registerForUpdate(); } public void resetPromptStatus() { this.packPromptStatus = ServerPackManager.PackPromptStatus.PENDING; } public void tick() { boolean bl = this.updateDownloads(); if (!bl) { this.triggerReloadIfNeeded(); } this.cleanupRemovedPacks(); } private void cleanupRemovedPacks() { this.packs.removeIf(serverPackData -> { if (serverPackData.activationStatus != ServerPackManager.ActivationStatus.INACTIVE) { return false; } else if (serverPackData.removalReason != null) { PackLoadFeedback.FinalResult finalResult = serverPackData.removalReason.serverResponse; if (finalResult != null) { this.packLoadFeedback.reportFinalResult(serverPackData.id, finalResult); } return true; } else { return false; } }); } private void onDownload(Collection packs, DownloadQueue.BatchResult batchResult) { if (!batchResult.failed().isEmpty()) { for (ServerPackManager.ServerPackData serverPackData : this.packs) { if (serverPackData.activationStatus != ServerPackManager.ActivationStatus.ACTIVE) { if (batchResult.failed().contains(serverPackData.id)) { serverPackData.setRemovalReasonIfNotSet(ServerPackManager.RemovalReason.DOWNLOAD_FAILED); } else { serverPackData.setRemovalReasonIfNotSet(ServerPackManager.RemovalReason.DISCARDED); } } } } for (ServerPackManager.ServerPackData serverPackDatax : packs) { Path path = (Path)batchResult.downloaded().get(serverPackDatax.id); if (path != null) { serverPackDatax.downloadStatus = ServerPackManager.PackDownloadStatus.DONE; serverPackDatax.path = path; if (!serverPackDatax.isRemoved()) { this.packLoadFeedback.reportUpdate(serverPackDatax.id, PackLoadFeedback.Update.DOWNLOADED); } } } this.registerForUpdate(); } private boolean updateDownloads() { List list = new ArrayList(); boolean bl = false; for (ServerPackManager.ServerPackData serverPackData : this.packs) { if (!serverPackData.isRemoved() && serverPackData.promptAccepted) { if (serverPackData.downloadStatus != ServerPackManager.PackDownloadStatus.DONE) { bl = true; } if (serverPackData.downloadStatus == ServerPackManager.PackDownloadStatus.REQUESTED) { serverPackData.downloadStatus = ServerPackManager.PackDownloadStatus.PENDING; list.add(serverPackData); } } } if (!list.isEmpty()) { Map map = new HashMap(); for (ServerPackManager.ServerPackData serverPackData2 : list) { map.put(serverPackData2.id, new DownloadQueue.DownloadRequest(serverPackData2.url, serverPackData2.hash)); } this.downloader.download(map, batchResult -> this.onDownload(list, batchResult)); } return bl; } private void triggerReloadIfNeeded() { boolean bl = false; final List list = new ArrayList(); final List list2 = new ArrayList(); for (ServerPackManager.ServerPackData serverPackData : this.packs) { if (serverPackData.activationStatus == ServerPackManager.ActivationStatus.PENDING) { return; } boolean bl2 = serverPackData.promptAccepted && serverPackData.downloadStatus == ServerPackManager.PackDownloadStatus.DONE && !serverPackData.isRemoved(); if (bl2 && serverPackData.activationStatus == ServerPackManager.ActivationStatus.INACTIVE) { list.add(serverPackData); bl = true; } if (serverPackData.activationStatus == ServerPackManager.ActivationStatus.ACTIVE) { if (!bl2) { bl = true; list2.add(serverPackData); } else { list.add(serverPackData); } } } if (bl) { for (ServerPackManager.ServerPackData serverPackData : list) { if (serverPackData.activationStatus != ServerPackManager.ActivationStatus.ACTIVE) { serverPackData.activationStatus = ServerPackManager.ActivationStatus.PENDING; } } for (ServerPackManager.ServerPackData serverPackDatax : list2) { serverPackDatax.activationStatus = ServerPackManager.ActivationStatus.PENDING; } this.reloadConfig.scheduleReload(new PackReloadConfig.Callbacks() { @Override public void onSuccess() { for (ServerPackManager.ServerPackData serverPackDatax : list) { serverPackDatax.activationStatus = ServerPackManager.ActivationStatus.ACTIVE; if (serverPackDatax.removalReason == null) { ServerPackManager.this.packLoadFeedback.reportFinalResult(serverPackDatax.id, PackLoadFeedback.FinalResult.APPLIED); } } for (ServerPackManager.ServerPackData serverPackDatax : list2) { serverPackDatax.activationStatus = ServerPackManager.ActivationStatus.INACTIVE; } ServerPackManager.this.registerForUpdate(); } @Override public void onFailure(boolean recoveryFailure) { if (!recoveryFailure) { list.clear(); for (ServerPackManager.ServerPackData serverPackDatax : ServerPackManager.this.packs) { switch (serverPackDatax.activationStatus) { case INACTIVE: serverPackDatax.setRemovalReasonIfNotSet(ServerPackManager.RemovalReason.DISCARDED); break; case PENDING: serverPackDatax.activationStatus = ServerPackManager.ActivationStatus.INACTIVE; serverPackDatax.setRemovalReasonIfNotSet(ServerPackManager.RemovalReason.ACTIVATION_FAILED); break; case ACTIVE: list.add(serverPackDatax); } } ServerPackManager.this.registerForUpdate(); } else { for (ServerPackManager.ServerPackData serverPackDatax : ServerPackManager.this.packs) { if (serverPackDatax.activationStatus == ServerPackManager.ActivationStatus.PENDING) { serverPackDatax.activationStatus = ServerPackManager.ActivationStatus.INACTIVE; } } } } @Override public List packsToLoad() { return list.stream().map(serverPackDatax -> new PackReloadConfig.IdAndPath(serverPackDatax.id, serverPackDatax.path)).toList(); } }); } } @Environment(EnvType.CLIENT) static enum ActivationStatus { INACTIVE, PENDING, ACTIVE; } @Environment(EnvType.CLIENT) static enum PackDownloadStatus { REQUESTED, PENDING, DONE; } @Environment(EnvType.CLIENT) public static enum PackPromptStatus { PENDING, ALLOWED, DECLINED; } @Environment(EnvType.CLIENT) static enum RemovalReason { DOWNLOAD_FAILED(PackLoadFeedback.FinalResult.DOWNLOAD_FAILED), ACTIVATION_FAILED(PackLoadFeedback.FinalResult.ACTIVATION_FAILED), DECLINED(PackLoadFeedback.FinalResult.DECLINED), DISCARDED(PackLoadFeedback.FinalResult.DISCARDED), SERVER_REMOVED(null), SERVER_REPLACED(null); @Nullable final PackLoadFeedback.FinalResult serverResponse; private RemovalReason(@Nullable final PackLoadFeedback.FinalResult serverResponse) { this.serverResponse = serverResponse; } } @Environment(EnvType.CLIENT) static class ServerPackData { final UUID id; final URL url; @Nullable final HashCode hash; @Nullable Path path; @Nullable ServerPackManager.RemovalReason removalReason; ServerPackManager.PackDownloadStatus downloadStatus = ServerPackManager.PackDownloadStatus.REQUESTED; ServerPackManager.ActivationStatus activationStatus = ServerPackManager.ActivationStatus.INACTIVE; boolean promptAccepted; ServerPackData(UUID id, URL url, @Nullable HashCode hash) { this.id = id; this.url = url; this.hash = hash; } public void setRemovalReasonIfNotSet(ServerPackManager.RemovalReason removalReason) { if (this.removalReason == null) { this.removalReason = removalReason; } } public boolean isRemoved() { return this.removalReason != null; } } }