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

+213
View File
@@ -0,0 +1,213 @@
//! 六维修复动作本体:枚举、维度、以及「代入捕获组 → agent 工具参数」的物化。
//!
//! 六个维度与 analyse/02 §7.7 的动作分类一一对应,也是 M16 `ToolSpec` 的参数来源
//! (契约见同目录 `CONTRACT.md` §3)。
use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use super::fix_params::{AdjustMemory, ChangeGraphics, ChangeJava, Reinstall, RemoveMod, SeekHelp};
use super::substitute;
/// 六维修复动作(YAML 里 `kind:` 选维度)。
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum FixAction {
/// 换 Java。
ChangeJava(ChangeJava),
/// 调内存。
AdjustMemory(AdjustMemory),
/// 改图形设置。
ChangeGraphics(ChangeGraphics),
/// 删/禁/换 mod。
RemoveMod(RemoveMod),
/// 重装/修组件。
Reinstall(Reinstall),
/// 求助上报。
SeekHelp(SeekHelp),
}
/// 六维动作的维度枚举(不含参数,便于判等/去重/映射工具)。
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum FixActionKind {
/// 换 Java。
ChangeJava,
/// 调内存。
AdjustMemory,
/// 改图形设置。
ChangeGraphics,
/// 删/禁/换 mod。
RemoveMod,
/// 重装/修组件。
Reinstall,
/// 求助上报。
SeekHelp,
}
impl FixActionKind {
/// 稳定字符串(诊断输出的 `kind` 字段,与 YAML 的 `kind:` 一致)。
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::ChangeJava => "change_java",
Self::AdjustMemory => "adjust_memory",
Self::ChangeGraphics => "change_graphics",
Self::RemoveMod => "remove_mod",
Self::Reinstall => "reinstall",
Self::SeekHelp => "seek_help",
}
}
/// 建议的 agent 工具 id。
///
/// M16 的 P0 工具只有 5 个(`get_config`/`set_config`/`list_game_sessions`/`read_game_log`/
/// `diagnose_crash`),因此它按 `kind` 桥接;这里的 id 是 P1 落地细分工具时的直接映射。
#[must_use]
pub const fn agent_tool(self) -> &'static str {
match self {
Self::ChangeJava => "java.set",
Self::AdjustMemory => "memory.set",
Self::ChangeGraphics => "graphics.set",
Self::RemoveMod => "mod.remove",
Self::Reinstall => "component.reinstall",
Self::SeekHelp => "diagnostics.report",
}
}
/// 全部六维(测试与 UI 遍历用)。
#[must_use]
pub const fn all() -> [Self; 6] {
[
Self::ChangeJava,
Self::AdjustMemory,
Self::ChangeGraphics,
Self::RemoveMod,
Self::Reinstall,
Self::SeekHelp,
]
}
}
/// 已做模板替换/单位换算的动作,可直接当 agent 工具参数。
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct MaterializedFixAction {
/// 维度。
pub kind: FixActionKind,
/// agent 工具 id(见 [`FixActionKind::agent_tool`])。
pub tool: &'static str,
/// 扁平字符串参数表。
pub args: BTreeMap<String, String>,
}
impl FixAction {
/// 维度。
#[must_use]
pub const fn kind(&self) -> FixActionKind {
match self {
Self::ChangeJava(_) => FixActionKind::ChangeJava,
Self::AdjustMemory(_) => FixActionKind::AdjustMemory,
Self::ChangeGraphics(_) => FixActionKind::ChangeGraphics,
Self::RemoveMod(_) => FixActionKind::RemoveMod,
Self::Reinstall(_) => FixActionKind::Reinstall,
Self::SeekHelp(_) => FixActionKind::SeekHelp,
}
}
/// 代入规则命名捕获组,产出 agent 工具可直接用的参数表。
#[must_use]
pub fn materialize(&self, groups: &BTreeMap<String, String>) -> MaterializedFixAction {
let kind = self.kind();
let mut args = BTreeMap::new();
match self {
Self::ChangeJava(p) => {
let derived = p
.class_file_version_group
.as_ref()
.and_then(|group| groups.get(group))
.and_then(|raw| raw.trim().parse::<u32>().ok())
.and_then(class_file_to_java_major);
put_opt(&mut args, "required_major", p.required_major.or(derived));
put_opt(&mut args, "max_major", p.max_major);
put_str(&mut args, "vendor", p.vendor.as_str());
put_opt_str(&mut args, "note", render_opt(&p.note, groups));
}
Self::AdjustMemory(p) => {
put_opt(&mut args, "suggested_xmx_mb", p.suggested_xmx_mb);
put_opt(&mut args, "min_xmx_mb", p.min_xmx_mb);
put_bool(&mut args, "need_64bit_java", p.need_64bit_java);
put_bool(&mut args, "increase_system_swap", p.increase_system_swap);
put_opt_str(&mut args, "note", render_opt(&p.note, groups));
}
Self::ChangeGraphics(p) => {
put_str(&mut args, "setting", p.setting.as_str());
put_bool(&mut args, "update_driver", p.update_driver);
put_opt_str(&mut args, "suggested_value", p.suggested_value.clone());
put_opt_str(&mut args, "note", render_opt(&p.note, groups));
}
Self::RemoveMod(p) => {
let ids = p
.mod_ids
.iter()
.map(|id| substitute(id, groups))
.collect::<Vec<_>>();
if !ids.is_empty() {
args.insert("mod_ids".to_owned(), ids.join(","));
}
put_str(&mut args, "action", p.action.as_str());
put_opt_str(&mut args, "note", render_opt(&p.note, groups));
}
Self::Reinstall(p) => {
put_str(&mut args, "component", p.component.as_str());
put_opt_str(&mut args, "path_hint", render_opt(&p.path_hint, groups));
put_opt_str(&mut args, "note", render_opt(&p.note, groups));
}
Self::SeekHelp(p) => {
put_bool(&mut args, "export_diagnostics", p.export_diagnostics);
if !p.needs_more_info.is_empty() {
args.insert("needs_more_info".to_owned(), p.needs_more_info.join("; "));
}
put_opt_str(&mut args, "note", render_opt(&p.note, groups));
}
}
MaterializedFixAction {
kind,
tool: kind.agent_tool(),
args,
}
}
}
/// 可选的模板字段:`Some` 时做 `{group}` 替换,`None` 保持 `None`。
fn render_opt(value: &Option<String>, groups: &BTreeMap<String, String>) -> Option<String> {
value.as_ref().map(|text| substitute(text, groups))
}
fn put_str(args: &mut BTreeMap<String, String>, key: &str, value: &str) {
args.insert(key.to_owned(), value.to_owned());
}
fn put_opt_str(args: &mut BTreeMap<String, String>, key: &str, value: Option<String>) {
if let Some(value) = value {
args.insert(key.to_owned(), value);
}
}
fn put_opt(args: &mut BTreeMap<String, String>, key: &str, value: Option<u32>) {
if let Some(value) = value {
args.insert(key.to_owned(), value.to_string());
}
}
fn put_bool(args: &mut BTreeMap<String, String>, key: &str, value: bool) {
args.insert(key.to_owned(), value.to_string());
}
/// `class file version 52` → `Java 8`(HMCL `getJavaVersionFromMajorVersion` 同式)。
#[must_use]
pub fn class_file_to_java_major(class_file_version: u32) -> Option<u32> {
class_file_version
.checked_sub(44)
.filter(|major| *major > 0)
}