feat: default user agent#389
Open
tnqzh123 wants to merge 2 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This Pull Request resolves #388 .
为 CustomSkinLoader 添加默认 User Agent,详见上述 Issue。
代码由 OpenAI Codex 使用 GPT 5.6 Sol 模型生成,由人类完成测试,不过没测 Quilt,因为 HMCL 的 Quilt 自动安装炸了。
以下是 Codex 生成的改动说明:(点击查看)
变更概述
本次变更为 CustomSkinLoader 的 HTTP 请求添加统一的默认 User-Agent,并允许 loadlist 和 ExtraList 中的自定义
userAgent使用运行时占位符。默认 User-Agent 至少包含以下信息:
默认格式如下:
示例:
同时,自定义 User-Agent 可以使用以下占位符:
变更背景
CustomSkinLoader 会向 Mojang API、第三方皮肤 API 和皮肤纹理地址发起 HTTP 请求。此前这些请求没有统一的默认 User-Agent,第三方皮肤服务端无法稳定获得客户端的 Minecraft、Mod 加载器、Java 和 CustomSkinLoader 版本信息。
loadlist 虽然已经提供
userAgent字段,但该字段只能使用固定字符串。服务端维护者如果希望在 User-Agent 中包含 Minecraft 或加载器版本,只能让用户手动填写,配置无法在不同游戏实例之间复用。本次变更的目标是:
HttpRequestUtil的网络请求拥有可识别的默认 User-Agent。在 Minecraft 26.1.2、Fabric Loader 0.19.2 环境的后续实测中,还发现 Fabric 加载器版本可能回退为
unknown。检查 Fabric Loader 0.19.2 后确认,其 jar Manifest 没有提供可用的Implementation-Version,因此原有的包版本回退路径无法覆盖该情况。本次变更同时补充了适用于 Fabric Loader 0.19.x 的版本兜底。改动前的行为
HttpRequest.userAgent的默认值为null。发送请求时,只有该字段不为null才会调用:因此改动前的行为如下:
userAgentHttpURLConnection实现{MINECRAFT_VERSION}等内容项目中也没有统一收集加载器名称、加载器版本和其他运行时版本信息的工具。
具体改动
1. 新增
UserAgentUtil新增
Common/src/main/java/customskinloader/utils/UserAgentUtil.java,集中负责:将这些逻辑放在独立工具类中,可以保证默认 User-Agent 和自定义占位符始终使用相同的数据来源和格式规则。
2. 生成默认 User-Agent
默认 User-Agent 使用以下运行时数据:
CustomSkinLoader.CustomSkinLoader_FULL_VERSIONMinecraftUtil.getMinecraftMainVersion()System.getProperty("java.version")使用
CustomSkinLoader_FULL_VERSION而不是基础版本,是为了让开发构建保留快照和构建编号。例如:正式构建仍会显示正常版本号:
3. 检测 Mod 加载器及版本
当前支持以下加载器:
现代 NeoForge 和 Forge 优先通过各自的
ModList查找加载器 Mod 容器,并从 Mod 元数据读取实际运行版本。如果 Mod 容器尚未提供版本,还会尝试以下回退接口:
NeoForgeVersionFMLLoader.versionInfo()ForgeVersionFMLLoader.versionInfo()net.minecraftforge.common.ForgeVersionFabric 和 Quilt 分别读取
fabricloader与quilt_loader的 Mod 元数据版本。Fabric 的首选路径在语义上等同于调用以下公开 API:由于该逻辑位于同时供多个加载器使用的 Common 模块中,实际实现通过反射调用这组公开 API,避免 Common 在编译期和类加载期直接依赖 Fabric Loader。
如果公开 Mod 元数据路径没有返回版本,Fabric 检测依次尝试:
net.fabricmc.loader.impl.FabricLoaderImpl.VERSION。FabricLoader所属包的Implementation-Version。unknown。FabricLoaderImpl.VERSION位于 Fabric Loader 的impl包,不属于其稳定公开 API,因此只作为兼容性兜底,不作为首选数据来源。Fabric Loader 0.19.2 的 jar Manifest 没有提供可用的Implementation-Version,只保留包版本回退会导致实测环境仍然得到unknown。这里查询的是 Mod ID
fabricloader,而不是fabric-api。Fabric API 是可选安装的 Mod,不是实际承载游戏的 Mod 加载器;将其发行版本写入{MOD_LOADER_VERSION}会使 User Agent 的含义不正确。所有加载器访问都通过反射完成。Common 模块因此不需要直接链接任意加载器 API,也不会因为缺少其他加载器的类而产生
ClassNotFoundException。加载器检测顺序为:
Forge 和 NeoForge 被放在 Fabric 之前,是为了兼容安装了 Fabric API 兼容层的环境。即使类路径中出现
FabricLoaderAPI,也应优先报告实际承载游戏的 Forge 或 NeoForge。4. 延迟缓存运行时信息
成功读取加载器名称和版本后,结果会缓存在不可变的
RuntimeInfo中。缓存字段使用
volatile,使多个皮肤加载线程能够安全读取已经初始化的结果。如果调用发生得过早,加载器元数据暂时只能返回
unknown,该结果不会被永久缓存。后续真正发送请求时会重新检测,避免启动早期的临时状态影响整个游戏生命周期。5. 统一接入 HTTP 请求
HttpRequestUtil.makeHttpRequest在建立连接前统一调用:随后始终设置最终 User-Agent:
将处理放在
HttpRequestUtil而不是分别修改每个 Profile Loader,可以覆盖现有及未来所有通过该工具发出的请求,并避免不同 Loader 产生不一致行为。目前
JsonAPILoader和LegacyLoader会将 loadlist 中的userAgent传入HttpRequest,因此这些请求可以展开自定义模板。没有传入自定义值的请求,例如现有 Mojang API 请求,会自动使用完整默认 User-Agent。6. 添加自定义占位符
loadlist 和 ExtraList 的
userAgent字段支持以下占位符:{DEFAULT_USER_AGENT}{CSL_VERSION}{MINECRAFT_VERSION}{MOD_LOADER_NAME}{MOD_LOADER_VERSION}{JAVA_VERSION}占位符采用精确的字面量替换,具有以下规则:
{}包裹。例如:
{ "name": "ExampleSkinServer", "type": "CustomSkinAPI", "root": "https://example.com/", "userAgent": "ExampleSkinClient/1.0 (CustomSkinLoader/{CSL_VERSION}; Minecraft/{MINECRAFT_VERSION}; {MOD_LOADER_NAME}/{MOD_LOADER_VERSION}; Java/{JAVA_VERSION})" }在示例 NeoForge 环境中会展开为:
也可以在自定义前缀或后缀中嵌入完整默认值:
{ "userAgent": "ExampleSkinClient/1.0 {DEFAULT_USER_AGENT}" }7. 规范化自动插入的 Header 字段
Minecraft、加载器、Java 和 CSL 版本来自运行时环境。为了避免空格、分号、换行或其他特殊字符破坏 HTTP Header,自动插入的字段会被规范化为合法的 HTTP token 字符。
例如:
无法读取或为空的字段会回退为:
该处理仅针对自动插入的版本组件。用户自定义 User-Agent 模板的其他部分不会被擅自修改。
8. 添加测试
Common/build.gradle新增 JUnit 4.13.2 测试依赖。新增
Common/src/test/java/customskinloader/utils/UserAgentUtilTest.java,覆盖:{DEFAULT_USER_AGENT}展开null时回退默认 User-Agent改动后的行为
userAgentnull{DEFAULT_USER_AGENT}为什么采用这种实现
在统一 HTTP 入口处理
所有请求最终都经过
HttpRequestUtil。在这里处理默认值和占位符,可以一次覆盖全部请求路径,也能确保未来新增的 Profile Loader 自动获得默认 User-Agent。使用反射检测加载器
项目需要同时支持跨度很大的 Minecraft 和加载器版本。Common 模块如果直接依赖 Forge、NeoForge、Fabric 或 Quilt 中的任意一个 API,会在其他环境中产生链接错误。
反射允许代码在运行时仅访问当前实际存在的加载器类,并为不同版本保留多个回退路径。
保留自定义字符串兼容性
没有占位符的现有
userAgent会原样发送,不需要用户迁移配置。显式空字符串的行为也没有改变。未知占位符不报错
保留未知占位符比删除或替换为空字符串更安全。这样既不会损坏用户配置,也允许第三方工具在发送前后继续处理自己的模板字段。
只缓存完整加载器信息
启动早期可能暂时拿不到加载器版本。如果立即缓存
unknown,后续所有请求都会携带错误信息。因此只有名称和版本均可用时才缓存结果。涉及文件
Common/src/main/java/customskinloader/utils/UserAgentUtil.javaCommon/src/main/java/customskinloader/utils/HttpRequestUtil.javaCommon/src/test/java/customskinloader/utils/UserAgentUtilTest.javaCommon/build.gradle测试与验证
执行完整构建:
验证结果:
UserAgentUtilTest共 6 项测试,全部通过。0.19.2。git diff --check通过。兼容性说明
userAgent的请求由“不显式设置”变为“发送完整默认 User-Agent”,这是本次变更的主要行为变化。Unknown/unknown,不会阻断皮肤加载。FabricLoaderImpl.VERSION仅用于公开路径未返回版本时的兼容性兜底。fabric-api不参与加载器版本检测,未安装 Fabric API 不会影响 User Agent 中 Fabric Loader 版本的读取。