AssetFetcher
AssetFetcher 负责 AssetDirector 的实现细节:与 Mojang 接口交互、维护版本索引与资源索引、按 SHA-1 校验下载物。类注释写明"最核心的是与 Mojang 的 API 打交道"。
基本信息
| 属性 | 值 |
|---|---|
| 类型 | 内部实现 / HTTP 下载器 |
| 所属 | AssetDirector 的字段 fetcher |
| 存放根目录 | MCUtil.getMCAssetsDir()(读)+ AssetDirector.AD_DIR(写) |
端点与路径
| 常量 | 值 | 覆盖方式 |
|---|---|---|
MANIFEST_ENDPOINT |
https://launchermeta.mojang.com/mc/game/version_manifest.json |
系统属性 assetDirector.manifestEndpoint |
RESOURCES_ENDPOINT |
https://resources.download.minecraft.net |
系统属性 assetDirector.resourcesEndpoint |
ASSET_INDEX_PATH |
assets/indexes/%s.json(参数为 assetsId) |
— |
CLIENT_JAR_PATH |
versions/%s/%s.jar(版本号,版本号) |
— |
VERSION_INDEX_PATH |
versions/%s/%s.json |
— |
DOWNLOAD_TIMEOUT |
10000 ms |
系统属性 assetDirector.downloadTimeout |
DOWNLOAD_ATTEMPTS |
3 |
系统属性 assetDirector.downloadAttempts |
资源对象路径为 /<hash 前两位>/<hash>,对应 Mojang 的 object store 布局。
公共方法
| 方法 | 说明 |
|---|---|
init() |
打印数据目录;删除所有 .part 结尾的残留文件(清理上次中断的下载);调用 SSLHacker.hack() |
loadVersionDeps(String version) |
依次准备:版本 JSON(必要时从 manifest 找 URL 下载)→ 构造 VersionIndex;若其 assetsId 未缓存则下载 assets/indexes/<assetsId>.json 并构造 AssetIndex |
fetchAsset(String version, String asset) |
准备版本依赖 → 查哈希 → 下载 |
fetchAssetByHash(String assetHash) |
已知哈希时直接下载;哈希为 null 时静默返回 |
fetchForAllVersions(String asset) |
对所有已加载 assetsId 的版本各下载一次 |
needsFetchResource(String version, String asset) |
needsFetchAsset(version, asset, false) |
needsFetchAsset(String version, String asset, boolean printErrors) |
查哈希后判断文件是否存在 |
needsFetchAssetByHash(String assetHash) |
哈希非 null 且文件不存在时返回 true |
getAssetHash(String version, String asset[, boolean printErrors]) |
从资源索引查名字到哈希的映射;printErrors 为真时查不到会报错 |
fileIsPresent(String hash) |
读路径不等于哨兵 NULL_FILE 即视为存在 |
fetchJar(String version) / loadJar(String version) / needsFetchJar(String version) |
客户端 jar 的下载、打开、存在性判断 |
getAssetFileForRead(String hash) |
先查内存 fileMap;未命中则依次试写目录 assets/objects/xx/<hash> 与原 assets 目录 objects/xx/<hash>,都不存在返回 NULL_FILE |
getAssetFileForWrite(String hash) |
恒为写目录 assets/objects/xx/<hash> |
getAssetInputStream(String hash) / getAssetInputStream(String version, String path) |
读流;后者先经 versionToAssetsId 换算 |
hashExists(String hash) |
内存缓存判断,哈希为 null 返回 false |
versionToAssetsId(String version) |
必要时先 loadVersionDeps |
getSha1(File file)(静态) |
Guava Files.hash(file, Hashing.sha1()) |
下载与校验
downloadAssetByHash 的流程:
- 下载到
<写目录>/<hash>.part; - 比对 SHA-1;
- 匹配则建父目录、
renameTo正式文件、写入fileMap、break; - 不匹配则
LOGGER.warn("Got invalid hash when downloading <hash>. Attempt <i>/<DOWNLOAD_ATTEMPTS>")继续重试。
版本索引下载用 FileUtils.copyURLToFile(url, destination, DOWNLOAD_TIMEOUT, DOWNLOAD_TIMEOUT);manifest 按 versions[].id 匹配目标版本,取其 url 下载,版本不存在则 LOGGER.error("Game version <version> could not be found in manifest json.")。
内部数据结构
| 类型 | 字段 / 说明 |
|---|---|
AssetFetcher.AssetIndex(public static) |
public JsonObject json、public Map<String, String> nameToHash;构造时遍历 json.objects 读每项的 hash |
AssetFetcher.VersionIndex(public 内部类) |
public JsonObject json、public String assetsId(取 json.assets)、JarFile jar、public Set<String> jarContents、public Version version(取 json.id);方法 fetchJar、loadJar、jarContainsFile(String)、getJarFileStream(String) |
ManifestVersionJSON(private static) |
字段 id、url |
getJarFileStream 在 jar 未加载时会对 null jar 抛 NPE——调用方需保证先 loadJar。
并发
无。整个 AssetDirector 流程在 pre-init 阶段单线程同步执行,靠 FML 的 ProgressManager.ProgressBar 显示进度(封装见 SSL 信任库替换 中的 MCUtil.ProgressBar)。
相关条目
- AssetDirector - 调用方与
ADConfig声明格式 - 多版本原版资源包 - 消费
versionIndexes/assetIndexes/hashExists/getAssetInputStream - SSL 信任库替换 -
init()里调用的SSLHacker.hack()与MCUtil