diff --git a/crates/core/src/addon/online/download.rs b/crates/core/src/addon/online/download.rs index 8c2716a..9213a4c 100644 --- a/crates/core/src/addon/online/download.rs +++ b/crates/core/src/addon/online/download.rs @@ -47,21 +47,24 @@ use super::transport::{OnlineError, OnlineErrorKind, OnlineFuture}; /// 那个默认值会因为**用户的**配置而变);这里的 3 是内容库元数据请求的产品口径,没有设置键,也不该 /// 被某个设置项的出厂默认牵着走。 /// -/// **边界**:最坏情况 = [`RETRY_ATTEMPTS`](外层)× [`DEFAULT_MAX_ATTEMPTS_PER_SOURCE`](引擎每个源 -/// 一次请求内的尝试)= 9 次真实网络请求。下面两条注解级断言(编译期求值)钉住这个乘积:任一侧想改, -/// 都得先读到这段判据,而不是被一次「统一常量」的顺带改动悄悄翻倍。 +/// **边界**:真实请求数 = [`RETRY_ATTEMPTS`](外层)× 引擎每源预算(`download.retry_limit`,出厂 +/// [`DEFAULT_MAX_ATTEMPTS_PER_SOURCE`])。**出厂档**是 3 × 3 = 9;用户把 `retry_limit` 调到 N 时是 +/// 3 × N(schema 上限 `MAX_ATTEMPTS_PER_SOURCE` = 10 ⇒ 30)——**9 只是出厂档的数,不是事实上限**, +/// 这正是本仓反复吃亏的「把出厂值说成上限」。下面两条注解级断言(编译期求值)钉的也只是**出厂档**: +/// 出厂默认想改,就得先读到这段判据,而不是被一次「统一常量」的顺带改动悄悄翻倍。 pub const RETRY_ATTEMPTS: u32 = 3; /// 外层预算被改了吗(改之前先读 [`RETRY_ATTEMPTS`] 的 doc:它不是引擎的每源次数)。 const _: () = assert!( RETRY_ATTEMPTS == 3, - "外层下载预算变了:它不是 download::config 的每源尝试次数,改它要单独一个决定" + "外层下载预算(出厂档 3)变了:它不是 download::config 的每源尝试次数,改它要单独一个决定" ); -/// 最坏情况下的真实请求数(3 × 3)——外层与引擎每源这两个「3」不共享来源,乘积才是事实。 +/// **出厂档**最坏请求数(外层 3 × 引擎每源出厂 3)——两个「3」不共享来源,乘积才是事实。 +/// 用户把 `download.retry_limit` 调大后真实最坏是 3 × N,与本断言无关(那头是运行时配置,编译期钉不了)。 const _: () = assert!( RETRY_ATTEMPTS as u64 * DEFAULT_MAX_ATTEMPTS_PER_SOURCE as u64 == 9, - "最坏真实请求数变了(外层 × 引擎每源):先读 RETRY_ATTEMPTS 的 doc 再改" + "出厂档最坏真实请求数变了(外层 3 × 引擎每源出厂 3 = 9;用户把 retry_limit 调到 N 时是 3 × N):先读 doc" ); /// 一次下载请求(只含「引擎需要什么」,不含任何站点字段)。 diff --git a/crates/core/src/download/config.rs b/crates/core/src/download/config.rs index 331dbf4..832d2ac 100644 --- a/crates/core/src/download/config.rs +++ b/crates/core/src/download/config.rs @@ -35,9 +35,9 @@ pub const KEY_CONCURRENCY: &str = "download.concurrency"; pub const KEY_RETRY_LIMIT: &str = "download.retry_limit"; /// gh-proxy 前缀覆盖键(三态语义见 [`gh_proxy_override`])。**已登记**:`setting::keys::proxy` 的 /// `specs()` 里有它(文本 / 出厂空串),presenter 与 CLI 的宿主都按这个常量读,读不到 ⇒ -/// [`GhProxyOverride::Builtin`]。**弱在哪(别当死键删)**:本文件没有断言把它与 schema 登记绑起来, -/// 常量值被改名或写错时这里不红,行为只会在宿主侧静默退化成 `Builtin`(用户填的 `off` 被忽略); -/// 拦住行为退化的是 `empty_gh_proxy_means_follow_builtin_not_off` 与 `shared` 的「调优 → 候选链」。 +/// [`GhProxyOverride::Builtin`]。**绑定在哪(别当死键删)**:常量与 schema 登记的对应关系由 +/// `setting::keys::proxy` 的 `the_gh_proxy_key_is_bound_to_the_schema_registration`(M205)钉住:写错就红; +/// 它抓不住两侧同时改名(公开改名)。行为退化由 `empty_gh_proxy_means_follow_builtin_not_off` 与 `shared` 的候选链拦。 pub const KEY_GH_PROXY: &str = "download.gh_proxy"; /// 引擎配置。 diff --git a/crates/presenter/src/host/addon/http.rs b/crates/presenter/src/host/addon/http.rs index 00b93e5..677c36a 100644 --- a/crates/presenter/src/host/addon/http.rs +++ b/crates/presenter/src/host/addon/http.rs @@ -25,18 +25,20 @@ use unlucky_core::download::{DownloadRequest, ResourceKind}; use super::super::env::HostEnv; -/// 站点要求的 User-Agent(Modrinth 明文要求带联系方式,否则限流)。 +/// 站点要求的 User-Agent(`(+联系方式)` 这一段来自 Modrinth 的 UA 政策:带联系方式,否则限流)。 /// /// ## 为什么这条 UA 要单独看 /// -/// 它**只**出现在带 header 的那条路径(`engine.client()` 直发,今天是 CurseForge 的 `X-API-KEY`), -/// 而 Modrinth 的请求走 [`get_via_engine`] —— 那条用的是引擎自己的 UA(`EngineConfig::user_agent`)。 -/// 也就是说进程里同时存在两个 UA 字符串,改一处不会带动另一处。 +/// 它**只**出现在带 header 的那条路径(`engine.client()` 直发),而今天走这条路径的只有 CurseForge 的 +/// 搜索(`x-api-key`)。**Modrinth 看到的是另一个 UA**:内容库的 Modrinth 请求经 +/// `core::addon::online::EngineTransport` → `DownloadEngine::download_bytes`,带的是引擎自己的 +/// `EngineConfig::user_agent`(`UnluckyLauncher/`,无联系方式)。两条 UA 同时活着,改一处不带另一处; +/// [`get_via_engine`] 那条无 header 的分支今天只有单测在用(生产里 Modrinth 不走它)。 /// /// ## 版本号来自 `CARGO_PKG_VERSION`(M205) /// /// 此前写死 `Unlucky-Launcher/0.1`,而当时的版本已经是 0.2.3:写死的版本号只会一年一年地落后,而 -/// Modrinth 按 UA 做限流与「出问题怎么联系你」的登记 —— UA 里说错版本,就是给了错信息。 +/// 站点按 UA 做限流与「出问题怎么联系你」的登记 —— UA 里说错版本,就是给了错信息。 /// /// **产品名与联系方式的写法不跟着版本动**:`Unlucky-Launcher/ (+)` 这个形状、 /// 以及那个 GitHub 地址,是站点侧识别我们、以及知道去哪儿联系的地方;改它是单独的产品决定,不在