mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 13:54:53 +08:00
feat(assetbundle): 完善官方资源解析与双目录发布
This commit is contained in:
@@ -52,7 +52,7 @@ const BEIJING_UTC_OFFSET_SECONDS: u64 = 8 * 60 * 60;
|
||||
const DAILY_FORCED_REFRESH_LOCAL_SECONDS: [u64; 3] = [3 * 60 * 60, 16 * 60 * 60, 18 * 60 * 60];
|
||||
const DAILY_FORCED_REFRESH_LABEL: &str = "UTC+8 03:00, 16:00, 18:00";
|
||||
const STARTUP_BANNER: &str = r#"
|
||||
============================================================
|
||||
=====================================================================================
|
||||
____ _ _ _ _ _____ _ _ _ _
|
||||
| __ )| |_ _ ___ / \ _ __ ___| |__ (_)_ _____|_ _|__ ___ | | | _(_) |_
|
||||
| _ \| | | | |/ _ \/ _ \ | '__/ __| '_ \| \ \ / / _ \ | |/ _ \ / _ \| | |/ / | __|
|
||||
@@ -61,7 +61,7 @@ const STARTUP_BANNER: &str = r#"
|
||||
|
||||
BlueArchiveToolkit
|
||||
Official Resource Sync
|
||||
============================================================
|
||||
=====================================================================================
|
||||
"#;
|
||||
|
||||
fn main() {
|
||||
@@ -3104,6 +3104,7 @@ fn sync_command_rpc_method(options: &CliOptions, command_name: &str) -> Option<&
|
||||
&& options.config.launcher_version == defaults.launcher_version
|
||||
&& options.config.platforms.is_none()
|
||||
&& options.config.output_root == defaults.output_root
|
||||
&& options.config.localized_output_root == defaults.localized_output_root
|
||||
&& options.config.snapshot_path.is_none()
|
||||
&& options.config.curl_command == defaults.curl_command
|
||||
&& options.config.curl_proxy == defaults.curl_proxy
|
||||
@@ -3122,6 +3123,7 @@ fn sync_command_rpc_method(options: &CliOptions, command_name: &str) -> Option<&
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn refresh_should_use_daemon_rpc(options: &CliOptions, command_name: &str) -> bool {
|
||||
sync_command_rpc_method(options, command_name) == Some(RPC_METHOD_REFRESH)
|
||||
}
|
||||
@@ -3456,7 +3458,14 @@ impl HumanReport for OfficialUpdateReport {
|
||||
print_field("本地审计", format_bool(self.audit_local));
|
||||
print_field("自动修复", format_bool(self.repair));
|
||||
print_field("dry-run", format_bool(self.dry_run));
|
||||
print_path_field("输出根目录", &self.output_root);
|
||||
print_path_field("官方资源目录", &self.output_root);
|
||||
print_path_field("汉化输出目录", &self.localized_output_root);
|
||||
print_field("汉化发布状态", self.localized_release_status.as_str());
|
||||
print_path_field("汉化 current", &self.localized_current_path);
|
||||
print_optional_path_field(
|
||||
"汉化 published",
|
||||
self.localized_published_version_path.as_ref(),
|
||||
);
|
||||
print_path_field("active release", &self.active_resource_root);
|
||||
print_path_field("current", &self.current_path);
|
||||
print_path_field("version state", &self.version_state_path);
|
||||
@@ -3464,6 +3473,7 @@ impl HumanReport for OfficialUpdateReport {
|
||||
print_optional_path_field("published", self.published_version_path.as_ref());
|
||||
print_path_field("snapshot", &self.snapshot_path);
|
||||
print_path_field("manifest", &self.download_manifest);
|
||||
print_optional_path_field("解析缓存", self.parse_cache_path.as_ref());
|
||||
print_optional_path_field("写入 snapshot", self.snapshot_written.as_ref());
|
||||
print_optional_path_field("bootstrap cache", self.bootstrap_cache_path.as_ref());
|
||||
print_optional_field("bootstrap 命中", self.bootstrap_cache_hit.map(format_bool));
|
||||
@@ -3478,6 +3488,14 @@ impl HumanReport for OfficialUpdateReport {
|
||||
print_field("需修复", self.local_manifest_repair_needed_count);
|
||||
print_field("官方 hash 校验", self.official_seed_hash_verified_count);
|
||||
print_verification_summary(&self.verification_summary);
|
||||
if let Some(summary) = self.parse_summary.as_ref() {
|
||||
print_field("解析缓存条目", summary.cache_entry_count);
|
||||
print_field("解析成功 bundle", summary.parsed_bundle_count);
|
||||
print_field("解析复用", summary.skipped_unchanged_count);
|
||||
print_field("解析不支持", summary.unsupported_count);
|
||||
print_field("解析失败", summary.failed_count);
|
||||
print_field("TextAsset", summary.text_asset_count);
|
||||
}
|
||||
print_field("catalog marker", self.addressables_marker_checked_count);
|
||||
print_list("变更 endpoint", &self.changed_endpoint_urls, 8);
|
||||
print_list("计划 URL", &self.download_urls, 8);
|
||||
@@ -3868,11 +3886,21 @@ fn build_doctor_report(
|
||||
let mut checks = vec![
|
||||
path_check("state_dir", state_dir, "后台状态目录可用"),
|
||||
path_check("output_root", &config.output_root, "资源输出目录可用"),
|
||||
path_check(
|
||||
"localized_output_root",
|
||||
&config.localized_output_root,
|
||||
"汉化输出目录可用",
|
||||
),
|
||||
safety_check(
|
||||
"output_root_safety",
|
||||
validate_output_root(&config.output_root),
|
||||
"资源输出目录安全边界通过",
|
||||
),
|
||||
safety_check(
|
||||
"localized_output_root_safety",
|
||||
validate_output_root(&config.localized_output_root),
|
||||
"汉化输出目录安全边界通过",
|
||||
),
|
||||
safety_check(
|
||||
"state_dir_safety",
|
||||
validate_runtime_state_dir(state_dir),
|
||||
@@ -4524,6 +4552,8 @@ fn daemon_child_args(options: &CliOptions) -> Vec<String> {
|
||||
}
|
||||
args.push("--output".to_string());
|
||||
args.push(config.output_root.to_string_lossy().to_string());
|
||||
args.push("--localized-output".to_string());
|
||||
args.push(config.localized_output_root.to_string_lossy().to_string());
|
||||
if let Some(snapshot_path) = config.snapshot_path.as_ref() {
|
||||
args.push("--snapshot".to_string());
|
||||
args.push(snapshot_path.to_string_lossy().to_string());
|
||||
@@ -4937,6 +4967,7 @@ fn stage_to_static(stage: &str) -> &'static str {
|
||||
"download" => "download",
|
||||
"snapshot" => "snapshot",
|
||||
"publish" => "publish",
|
||||
"parse" => "parse",
|
||||
"finish" => "finish",
|
||||
"watch" => "watch",
|
||||
"daemon" => "daemon",
|
||||
@@ -4968,6 +4999,7 @@ fn localized_stage(stage: &str) -> &str {
|
||||
"dry-run" => "试运行",
|
||||
"download" => "下载",
|
||||
"publish" => "发布",
|
||||
"parse" => "解析",
|
||||
"resource" => "资源",
|
||||
"finish" => "完成",
|
||||
"watch" => "常驻",
|
||||
@@ -5003,8 +5035,10 @@ const ENV_TEMPLATE: &str = r#"# BlueArchive Toolkit 配置文件(bat 首次启
|
||||
# 设 BAT_SKIP_ENV_FILE=1 可让 bat 完全忽略本文件。
|
||||
|
||||
# ---- 基本配置 ----
|
||||
# 资源发布根目录(默认 ./bat-resources,相对当前工作目录)
|
||||
# 官方原版资源发布根目录(默认 ./bat-resources,相对当前工作目录)
|
||||
BAT_OUTPUT=./bat-resources
|
||||
# 汉化产物输出根目录(默认 ./bat-localized,与官方原版资源分离)
|
||||
BAT_LOCALIZED_OUTPUT=./bat-localized
|
||||
# 自动发现 app-version / connection-group / server-info(无参启动建议保持 1)
|
||||
BAT_AUTO_DISCOVER=1
|
||||
# 后台状态目录(bat.sock / 日志 / 任务历史等;默认 /tmp/bat-pid)
|
||||
@@ -5175,6 +5209,9 @@ fn apply_bat_env_overrides(
|
||||
if let Some(v) = value("BAT_OUTPUT") {
|
||||
options.config.output_root = PathBuf::from(v);
|
||||
}
|
||||
if let Some(v) = value("BAT_LOCALIZED_OUTPUT") {
|
||||
options.config.localized_output_root = PathBuf::from(v);
|
||||
}
|
||||
if let Some(v) = value("BAT_STATE_DIR") {
|
||||
options.state_dir = PathBuf::from(v);
|
||||
}
|
||||
@@ -5343,6 +5380,11 @@ fn parse_args_with_env(
|
||||
options.config.output_root = PathBuf::from(next_option_value(&mut args, &flag)?);
|
||||
options.output_explicit = true;
|
||||
}
|
||||
"--localized-output" => {
|
||||
options.config.localized_output_root =
|
||||
PathBuf::from(next_option_value(&mut args, &flag)?);
|
||||
options.output_explicit = true;
|
||||
}
|
||||
"--state-dir" | "--pid-dir" => {
|
||||
options.state_dir = PathBuf::from(next_option_value(&mut args, &flag)?);
|
||||
}
|
||||
@@ -5707,7 +5749,10 @@ fn print_usage(binary: &str) {
|
||||
eprintln!("Sync:");
|
||||
eprintln!(" --platforms <LIST> Platforms, e.g. Windows,Android");
|
||||
eprintln!(
|
||||
" --output <DIR> Resource publish root (default: ./bat-resources)"
|
||||
" --output <DIR> Official resource publish root (default: ./bat-resources)"
|
||||
);
|
||||
eprintln!(
|
||||
" --localized-output <DIR> Localized output root (default: ./bat-localized)"
|
||||
);
|
||||
eprintln!(" --snapshot <PATH> Override snapshot path (default: <output>/current/official-sync-snapshot.json)");
|
||||
eprintln!(" --curl <PATH> curl executable (default: curl)");
|
||||
@@ -5739,7 +5784,10 @@ fn print_usage(binary: &str) {
|
||||
eprintln!();
|
||||
eprintln!("Defaults:");
|
||||
eprintln!(" platforms: Windows,Android");
|
||||
eprintln!(" resource output: ./bat-resources (current -> versions/<id>, .staging/<id>)");
|
||||
eprintln!(
|
||||
" official resource output: ./bat-resources (current -> versions/<id>, .staging/<id>)"
|
||||
);
|
||||
eprintln!(" localized output: ./bat-localized (separate patch/export target)");
|
||||
eprintln!(" daemon state: /tmp/bat-pid (bat.sock, bat.pid, bat-status.json, bat-daemon.log, bat-events.jsonl)");
|
||||
eprintln!(" forced refresh: {DAILY_FORCED_REFRESH_LABEL}");
|
||||
}
|
||||
@@ -5820,6 +5868,7 @@ mod tests {
|
||||
&["bat"],
|
||||
&[
|
||||
("BAT_OUTPUT", "/srv/bat"),
|
||||
("BAT_LOCALIZED_OUTPUT", "/srv/bat-localized"),
|
||||
("BAT_AUTO_DISCOVER", "1"),
|
||||
("BAT_STATE_DIR", "/srv/state"),
|
||||
("BAT_INTERVAL_SECONDS", "120"),
|
||||
@@ -5827,6 +5876,10 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(options.config.output_root, PathBuf::from("/srv/bat"));
|
||||
assert_eq!(
|
||||
options.config.localized_output_root,
|
||||
PathBuf::from("/srv/bat-localized")
|
||||
);
|
||||
assert!(options.config.auto_discover);
|
||||
assert_eq!(options.state_dir, PathBuf::from("/srv/state"));
|
||||
assert_eq!(options.interval, Duration::from_secs(120));
|
||||
@@ -5946,6 +5999,7 @@ mod tests {
|
||||
keys.push(key);
|
||||
}
|
||||
assert!(keys.contains(&"BAT_OUTPUT".to_string()));
|
||||
assert!(keys.contains(&"BAT_LOCALIZED_OUTPUT".to_string()));
|
||||
assert!(keys.contains(&"BAT_AUTO_DISCOVER".to_string()));
|
||||
}
|
||||
|
||||
@@ -6118,6 +6172,10 @@ mod tests {
|
||||
assert!(options.banner);
|
||||
assert_eq!(options.output_format, OutputFormat::Human);
|
||||
assert_eq!(config.output_root, PathBuf::from("./bat-resources"));
|
||||
assert_eq!(
|
||||
config.localized_output_root,
|
||||
PathBuf::from("./bat-localized")
|
||||
);
|
||||
assert_eq!(options.state_dir, PathBuf::from(DEFAULT_DAEMON_STATE_DIR));
|
||||
}
|
||||
|
||||
@@ -6210,6 +6268,8 @@ mod tests {
|
||||
"--daemon",
|
||||
"--output",
|
||||
"/tmp/daemon-output",
|
||||
"--localized-output",
|
||||
"/tmp/daemon-localized",
|
||||
"--interval",
|
||||
"30m",
|
||||
])
|
||||
@@ -6222,6 +6282,10 @@ mod tests {
|
||||
options.config.output_root,
|
||||
PathBuf::from("/tmp/daemon-output")
|
||||
);
|
||||
assert_eq!(
|
||||
options.config.localized_output_root,
|
||||
PathBuf::from("/tmp/daemon-localized")
|
||||
);
|
||||
assert_eq!(options.state_dir, PathBuf::from(DEFAULT_DAEMON_STATE_DIR));
|
||||
assert_eq!(options.interval, Duration::from_secs(30 * 60));
|
||||
assert!(options.quiet_up_to_date);
|
||||
@@ -6246,11 +6310,19 @@ mod tests {
|
||||
assert!(!status.progress);
|
||||
assert!(!status.banner);
|
||||
assert_eq!(status.config.output_root, PathBuf::from("./bat-resources"));
|
||||
assert_eq!(
|
||||
status.config.localized_output_root,
|
||||
PathBuf::from("./bat-localized")
|
||||
);
|
||||
assert_eq!(status.state_dir, PathBuf::from(DEFAULT_DAEMON_STATE_DIR));
|
||||
|
||||
let stop = parse(&["bat", "stop", "--state-dir", "/tmp/custom-bat-pid"]).unwrap();
|
||||
assert_eq!(stop.command, CliCommand::Stop);
|
||||
assert_eq!(stop.config.output_root, PathBuf::from("./bat-resources"));
|
||||
assert_eq!(
|
||||
stop.config.localized_output_root,
|
||||
PathBuf::from("./bat-localized")
|
||||
);
|
||||
assert_eq!(stop.state_dir, PathBuf::from("/tmp/custom-bat-pid"));
|
||||
}
|
||||
|
||||
@@ -6312,6 +6384,8 @@ mod tests {
|
||||
"Windows,Android",
|
||||
"--output",
|
||||
"/tmp/daemon-output",
|
||||
"--localized-output",
|
||||
"/tmp/daemon-localized",
|
||||
"--state-dir",
|
||||
"/tmp/daemon-state",
|
||||
"--curl",
|
||||
@@ -6336,6 +6410,9 @@ mod tests {
|
||||
assert!(args
|
||||
.windows(2)
|
||||
.any(|pair| pair == ["--output", "/tmp/daemon-output"]));
|
||||
assert!(args
|
||||
.windows(2)
|
||||
.any(|pair| pair == ["--localized-output", "/tmp/daemon-localized"]));
|
||||
assert!(args
|
||||
.windows(2)
|
||||
.any(|pair| pair == ["--state-dir", "/tmp/daemon-state"]));
|
||||
@@ -6579,8 +6656,10 @@ mod tests {
|
||||
let temp = tempfile::TempDir::new().unwrap();
|
||||
let state_dir = temp.path().join("state");
|
||||
let output_root = temp.path().join("output");
|
||||
let mut base_config = OfficialUpdateConfig::default();
|
||||
base_config.output_root = output_root;
|
||||
let base_config = OfficialUpdateConfig {
|
||||
output_root,
|
||||
..Default::default()
|
||||
};
|
||||
let (queue, _rx) = mpsc::channel::<TaskJob>();
|
||||
let context = DaemonTaskContext {
|
||||
registry: TaskRegistry::new(),
|
||||
@@ -6662,11 +6741,13 @@ mod tests {
|
||||
let control = new_daemon_control();
|
||||
// 保留 rx 让 send 成功(不启动 worker,任务停留在 queued)。
|
||||
let (queue, rx) = mpsc::channel::<TaskJob>();
|
||||
let mut base_config = OfficialUpdateConfig::default();
|
||||
base_config.force = true;
|
||||
base_config.dry_run = true;
|
||||
base_config.audit_local = false;
|
||||
base_config.repair = false;
|
||||
let base_config = OfficialUpdateConfig {
|
||||
force: true,
|
||||
dry_run: true,
|
||||
audit_local: false,
|
||||
repair: false,
|
||||
..Default::default()
|
||||
};
|
||||
let context = DaemonTaskContext {
|
||||
registry: TaskRegistry::new(),
|
||||
queue,
|
||||
|
||||
Reference in New Issue
Block a user