Unlucky Launcher v0.0.1-alpha
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s

Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent),
MCP server 接入外部 Agent 工具,GUI 开发中。
This commit is contained in:
AskaEth committed 2026-10-31 21:54:10 +08:00
commit bc41dfcf7d
517 files changed
+92844

No files matched your search

+90
View File
@@ -0,0 +1,90 @@
//! `instances list`:实例与已安装版本的总览。
//!
//! 安装/登记在 [`super::install`];这里只读(`profiles.json` + `versions/` 的存在性)。
//! 读 `profiles.json` 的实现委托给 `core::game::install::instance`,避免 CLI 自带一份
//! 解析逻辑(同一份文件两处解析迟早会漂移)。
use std::path::PathBuf;
use serde_json::{json, Value};
use unlucky_core::game::install::instance::read_profiles as read_profiles_from_layout;
use unlucky_core::game::ProfileList;
use crate::context::Ctx;
use crate::error::CliError;
use crate::output::Report;
/// `ull instances list`。
///
/// # Errors
///
/// `profiles.json` 读不了或解析失败时返回用户错误。
pub fn list(ctx: &Ctx) -> Result<Report, CliError> {
let profiles = read_profiles(ctx)?;
let game = ctx.game();
let installed = game.installed_versions().unwrap_or_default();
let mut rows: Vec<Value> = Vec::new();
let mut body: Vec<String> = Vec::new();
for profile in &profiles.profiles {
let present = game.is_version_installed(&profile.version_id);
body.push(format!(
" {} [{}] {} {}",
profile.name,
profile.version_id,
if present { "已安装" } else { "载荷缺失" },
profile.id.as_str()
));
rows.push(json!({
"id": profile.id.as_str(),
"name": profile.name,
"version_id": profile.version_id,
"version_present": present,
"game_dir": option_path(profile.game_dir.as_ref()),
"java_path": option_path(profile.java_path.as_ref()),
"last_played_at": profile.last_played_at,
}));
}
let data = json!({
"data_root": ctx.data.launcher_home.display().to_string(),
"game_root": ctx.data.game_home.display().to_string(),
"instances": rows,
"installed_versions": installed,
});
let mut human = vec![format!(
"实例 {} 个(数据根 {})",
profiles.profiles.len(),
ctx.data.launcher_home.display()
)];
if body.is_empty() {
human.push(" (空)用 `ull install <version>` 登记第一个实例".to_string());
}
human.extend(body);
if installed.is_empty() {
human.push("已安装版本: (无)".to_string());
} else {
human.push(format!("已安装版本: {}", installed.join(", ")));
}
Ok(Report::new(data, human))
}
/// 读实例清单(文件不存在 = 空列表,不算错误)。
///
/// # Errors
///
/// 读盘失败或 JSON 非法时返回用户错误。
pub fn read_profiles(ctx: &Ctx) -> Result<ProfileList, CliError> {
let path = ctx.profiles_config();
read_profiles_from_layout(&ctx.layout()).map_err(|error| {
CliError::user(
"profiles_parse",
format!("读实例清单 {} 失败:{error}", path.display()),
)
})
}
fn option_path(path: Option<&PathBuf>) -> Option<String> {
path.map(|path| path.display().to_string())
}