6.0 KiB
6.0 KiB
AMT - ARinera Minecraft Tool
Minecraft 实用 TUI 工具。Go + bubbletea,前后端分离。
文件结构
main.go 入口,解析 exe 目录,tea.NewProgram(WithAltScreen)
model.go 顶层 model 定义,page 枚举,Init/Update/View 路由
pages.go 4 个页面的 update/view + resetExecState + describeAction
scan.go 扫描 <exeDir>/*/.minecraft/versions/* 目录
api.go HTTP 客户端 + Action/StepResponse 类型定义
actions.go action 执行引擎(download/unzip/delete/copy/move/new/backup)
items.go list.Item 实现:versionItem, menuItem, mirrorItem
styles.go lipgloss 样式常量
backend/ Python FastAPI 后端(独立进程,端口 3131)
全部 Go 文件在项目根目录,package main。go build -o dist/amt.exe .
依赖
bubbleteav1.3.10 /bubblesv1.0.0 /lipglossv1.1.0- 标准库:
net/http,archive/zip,encoding/json,io,os,path/filepath
架构:页面状态机
pageVersionSelect ──Enter──▶ pageMainMenu ──"输入数字码"──▶ pageCodeInput ──Enter(4位)──▶ pageExecuting
▲ │ │ │ │
│ Esc/q=退出 │ Esc=返回 任意键=返回
└──"切换版本"─────────────┘ └──────────────────────────┘◀──────────────────────────┘
model 核心字段
| 字段 | 类型 | 用途 |
|---|---|---|
currentPage |
page (iota 枚举) |
当前页面 |
exeDir |
string | 可执行文件所在目录 |
versionDir |
string | 选中的版本完整路径 |
versionName |
string | 版本目录名 |
versionList |
list.Model | 版本选择列表(Init 时预初始化,避免零值 panic) |
menuList |
list.Model | 主菜单列表(同上) |
codeInput |
textinput.Model | 4 位数字码输入框 |
actions |
[]Action | 当前执行的 action 列表 |
actionIdx |
int | 当前执行到第几个 |
logLines |
[]string | 执行日志(也被 viewVersionSelect 用于显示扫描错误) |
execErr / execDone |
error / bool | 执行状态 |
choosingMirror |
bool | 是否在 mirror 选择子页面 |
pendingAction |
*Action | mirror 选择时暂存的 action |
backupDir |
string | 当次执行的备份相对路径 amt/backup/<timestamp> |
progressCh |
chan float64 | 下载进度 channel(预留) |
消息类型
| 消息 | 来源 | 处理页面 |
|---|---|---|
versionsFoundMsg |
scanVersions() |
pageVersionSelect |
scanErrorMsg |
scanVersions() |
pageVersionSelect |
actionsReceivedMsg |
fetchActions() |
pageExecuting |
apiErrorMsg |
fetchActions() |
pageExecuting |
actionCompleteMsg |
executeAction() |
pageExecuting |
actionErrorMsg |
executeAction() |
pageExecuting |
actionProgressMsg |
waitForProgress() |
pageExecuting |
mirrorChoiceMsg |
executeAdd() |
pageExecuting |
状态清理规则
关键约束:logLines 被 viewVersionSelect 用于判断是否显示错误,必须在离开相关上下文时清空。
- 进入 pageExecuting (T4):调用
resetExecState(m)清空所有执行状态 - 离开 pageExecuting (T6):同上
- 进入 pageVersionSelect (T3):清空
logLines+ 清空列表项 versionsFoundMsg到达时:清空logLines(覆盖旧扫描错误)- 离开 pageVersionSelect (T1):清空
logLines - mirror Esc 取消:清空
pendingAction
resetExecState 清空字段:actions, actionIdx, logLines, execErr, execDone, choosingMirror, pendingAction, backupDir, progressCh
后端 API
地址:http://localhost:3131
GET /tools?code=XXXX
返回 step 结构:
{
"actions": [
{"type": "add", "path": "相对路径", "unzip": false, "url": "https://...", "mirrors": ["https://..."]},
{"type": "delete", "path": "相对路径"},
{"type": "copy", "path": "源相对路径", "new_path": "目标相对路径"},
{"type": "move", "path": "源相对路径", "new_path": "目标相对路径"},
{"type": "new", "path": "相对路径", "is_dir": false}
]
}
所有 path 相对于 versionDir。
Action 执行逻辑 (actions.go)
顺序执行,任一失败立即中断。
| type | 行为 | 备份 |
|---|---|---|
add |
下载 url→path,unzip=true 时解压后删 zip | 目标已存在则备份 |
delete |
删除 path | 删前备份 |
copy |
复制 path→new_path | new_path 已存在则备份 |
move |
os.Rename,失败则 copy+delete | new_path 已存在则备份 |
new |
is_dir=true 创建目录,否则创建空文件(父目录自动创建)。目标已存在则跳过 | 无 |
备份
目录:<versionDir>/amt/backup/<YYYYMMDD_HHMMSS>/<原相对路径>
每次执行创建一个时间戳目录,同次执行的所有备份共享。
下载失败 Mirror 处理
主 URL 失败 → 发送 mirrorChoiceMsg → 设 choosingMirror=true → 显示 mirror 列表 → 用户选择后用新 URL 重试。用户 Esc 取消则中断执行。
关键函数
executeAction(versionDir, action, index, backupDir) → tea.Cmd → actionCompleteMsg | actionErrorMsg | mirrorChoiceMsg
executeAdd(...) add 专用,处理下载/解压/mirror
downloadFile(url, dest) 标准 http.Get + 临时文件 + rename
unzipFile(zip, dir) archive/zip 解压,含 zip slip 防护
backupPath(versionDir, relPath, backupDir) 备份单个文件/目录
copyPath(src, dst) 递归复制文件/目录
copyFile(src, dst) 单文件复制
copyDir(src, dst) filepath.Walk 递归复制
版本扫描 (scan.go)
filepath.Glob(exeDir/*/.minecraft/versions/*) → 过滤目录 → versionsFoundMsg
构建
go build -o dist/amt.exe . # 或 make
make backend # 启动后端 (uvicorn, port 3131)