diff --git a/README.en.md b/README.en.md index 80cb780..b1c80c7 100644 --- a/README.en.md +++ b/README.en.md @@ -261,7 +261,7 @@ Double-click `ModAVD.exe` in the package root: it starts a local server that lis ## Updating ModAVD -`modavd update` updates the whole ModAVD package in the root folder (the emulator, components and scripts, and also `ModAVD.exe` and `tools\modavd.exe` themselves). It does the same thing as AUTO-MAS's "ModAVD one-click update" (`魔改 AVD 一键更新`), and uses the same `\.update\` folder and state file: if an update is interrupted partway on either side, the other can finish it or roll it back. Under the current integration agreement, AUTO-MAS keeps its own updater for now; whether and when it switches to calling this command is up to the AUTO-MAS project's own maintenance, and this is not a feature or promise already delivered by this repository. +`modavd update` updates the whole ModAVD package in the root folder (the emulator, components and scripts, and also `ModAVD.exe` and `tools\modavd.exe` themselves). It does the same thing as AUTO-MAS's ModAVD one-click update, and uses the same `\.update\` folder and state file: if an update is interrupted partway on either side, the other can finish it or roll it back. Under the current integration agreement, AUTO-MAS keeps its own updater for now; whether and when it switches to calling this command is up to the AUTO-MAS project's own maintenance, and this is not a feature or promise already delivered by this repository. - It does only the step you explicitly ask for; it never goes online automatically and never downloads in the background: - `modavd update` (`status`) looks at local state only; `check` looks up the latest version; diff --git a/README.md b/README.md index 82eb8b1..1c91985 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ -[ModAVD(魔改 AVD)](https://github.com/ModAVD/ModAVD) 的管理程序:图形管理器 `ModAVD.exe`,以及命令行和 MCP 服务 `modavd.exe`。Go 写,只用标准库。目前只支持 Windows x64,平台相关的部分已经拆成平台层,其他平台的安排见 [ModAVD#27](https://github.com/ModAVD/ModAVD/issues/27)。 +[ModAVD](https://github.com/ModAVD/ModAVD) 的管理程序:图形管理器 `ModAVD.exe`,以及命令行和 MCP 服务 `modavd.exe`。Go 写,只用标准库。目前只支持 Windows x64,平台相关的部分已经拆成平台层,其他平台的安排见 [ModAVD#27](https://github.com/ModAVD/ModAVD/issues/27)。 > **版本提示(`main` 与 Release)**:本文按仓库 `main` 的实现编写。当前 `main` 的版本是 `0.3.0`,最新独立 Release 是 `v0.1.0`(对应 ModAVD `0.36.0`),`v0.2.0` 已起草、还没发布;`0.2.0` 起的新建后台初始化、`update` 命令和退出码 9 都不在 `v0.1.0` 下载包里。使用已发布包时,请以对应 Release 的说明和包内版本为准。 @@ -263,7 +263,7 @@ claude mcp add --transport stdio --scope user modavd '--' D:\ModAVD\tools\modavd ## 更新 ModAVD -`modavd update` 更新根目录里的整个 ModAVD 包(模拟器、组件、脚本,也包括 `ModAVD.exe` 和 `tools\modavd.exe` 自己),和 AUTO-MAS 的「魔改 AVD 一键更新」做的是同一件事,用的也是同一个 `<根>\.update\` 目录和状态文件:哪边更新到一半被打断,另一边都能接着补完或退回。按当前对接约定,AUTO-MAS 目前保留自己的更新器;是否以及何时改成调用这个命令,由 AUTO-MAS 项目自己的维护决定,这不是本仓库已经交付的功能或承诺。 +`modavd update` 更新根目录里的整个 ModAVD 包(模拟器、组件、脚本,也包括 `ModAVD.exe` 和 `tools\modavd.exe` 自己),和 AUTO-MAS 里的 ModAVD 一键更新做的是同一件事,用的也是同一个 `<根>\.update\` 目录和状态文件:哪边更新到一半被打断,另一边都能接着补完或退回。按当前对接约定,AUTO-MAS 目前保留自己的更新器;是否以及何时改成调用这个命令,由 AUTO-MAS 项目自己的维护决定,这不是本仓库已经交付的功能或承诺。 - 只做明确要求的那一步,不自动联网、不在后台下载: - `modavd update`(`status`)只看本地;`check` 查最新版本; diff --git a/cmd/modavd-gui/main.go b/cmd/modavd-gui/main.go index 3565eb2..487db61 100644 --- a/cmd/modavd-gui/main.go +++ b/cmd/modavd-gui/main.go @@ -111,7 +111,7 @@ func run(args []string) (code int) { fs := flag.NewFlagSet("ModAVD", flag.ContinueOnError) fs.SetOutput(io.Discard) - rootFlag := fs.String("root", "", "魔改 AVD 根目录") + rootFlag := fs.String("root", "", "ModAVD 根目录") port := fs.Int("port", 0, "监听端口(0 = 随机)") noWindow := fs.Bool("no-window", false, "不打开窗口") dev := fs.Bool("dev", false, "开发模式:提供 /dev/ 的模拟后端页面,没有窗口也不自动退出") @@ -134,7 +134,7 @@ func run(args []string) (code int) { } r, err := root.Resolve(*rootFlag) if err != nil { - fatalErr("找不到魔改 AVD 根目录", err) + fatalErr("找不到 ModAVD 根目录", err) return errs.ExitCode(err) } diff --git a/internal/adb/adb.go b/internal/adb/adb.go index 26bc645..7d425d8 100644 --- a/internal/adb/adb.go +++ b/internal/adb/adb.go @@ -226,7 +226,7 @@ var ( func (c *Client) portConflict(k serverKind, port int, pid uint32, name string) *errs.Error { return errs.Newf(errs.PortConflict, - "魔改 AVD 的%s adb 端口 %d 被 %s(PID %d)占用,"+serverConflictReason+"。请在 %s 里把 %s 改成一个没被占用的端口"+ + "ModAVD 的%s adb 端口 %d 被 %s(PID %d)占用,"+serverConflictReason+"。请在 %s 里把 %s 改成一个没被占用的端口"+ "(不能用 5037 和各实例的控制台 / adb / gRPC 端口,也不能和 %s 相同),或者先关掉占用它的程序", k.label, port, name, pid, c.Root.MetadataPath(), k.key, k.otherKey) } @@ -325,7 +325,7 @@ func (c *Client) ensure(ctx context.Context, k serverKind, port int, timeout tim return false, err } prepareServerLog(c.Root) - c.Log.Infof("启动魔改 AVD %s adb server(端口 %d)", k.label, port) + c.Log.Infof("启动 ModAVD %s adb server(端口 %d)", k.label, port) // StartOrphan rather than StartDetached: an MCP client ends modavd's whole process tree when it disconnects, and // while the start-server client (our child for ~2 s) is alive the server it forks is part of that tree. Losing // the server is not harmless: the emulator registers with it only once, at boot. Without a configured orphan @@ -333,7 +333,7 @@ func (c *Client) ensure(ctx context.Context, k serverKind, port int, timeout tim client, err := host.StartOrphan(c.Root.AdbExe(), []string{"-P", strconv.Itoa(port), "start-server"}, host.StartOptions{Env: c.Env()}) if err != nil { - return false, errs.Wrap(errs.Internal, err, fmt.Sprintf("启动魔改 AVD %s adb server(端口 %d)失败: %s", + return false, errs.Wrap(errs.Internal, err, fmt.Sprintf("启动 ModAVD %s adb server(端口 %d)失败: %s", k.label, port, errs.As(err).Message)) } up, err = c.waitListening(ctx, k, port, timeout, client) diff --git a/internal/adb/adb_test.go b/internal/adb/adb_test.go index 8b66619..56d953d 100644 --- a/internal/adb/adb_test.go +++ b/internal/adb/adb_test.go @@ -351,14 +351,14 @@ func TestPortConflictMessage(t *testing.T) { dir := tempDir(t) c := New(&root.Root{Dir: dir}, nil) e := c.portConflict(privateServer, 20050, 1234, "foo.exe") - want := "魔改 AVD 的私有 adb 端口 20050 被 foo.exe(PID 1234)占用,它不是 adb。请在 " + c.Root.MetadataPath() + + want := "ModAVD 的私有 adb 端口 20050 被 foo.exe(PID 1234)占用,它不是 adb。请在 " + c.Root.MetadataPath() + " 里把 adbServerPort 改成一个没被占用的端口(不能用 5037 和各实例的控制台 / adb / gRPC 端口,也不能和 " + "scriptAdbServerPort 相同),或者先关掉占用它的程序" if e.Code != errs.PortConflict || e.Error() != want { t.Errorf("private conflict:\n got %s %q\nwant %q", e.Code, e.Error(), want) } e = c.portConflict(scriptServer, 20049, 1, "x.exe") - if !strings.HasPrefix(e.Message, "魔改 AVD 的脚本专用 adb 端口 20049 被 x.exe(PID 1)占用") || + if !strings.HasPrefix(e.Message, "ModAVD 的脚本专用 adb 端口 20049 被 x.exe(PID 1)占用") || !strings.Contains(e.Message, "里把 scriptAdbServerPort 改成") || !strings.Contains(e.Message, "也不能和 adbServerPort 相同") { t.Errorf("script conflict: %q", e.Message) } diff --git a/internal/adb/real_adb_test.go b/internal/adb/real_adb_test.go index e003776..c6b2c22 100644 --- a/internal/adb/real_adb_test.go +++ b/internal/adb/real_adb_test.go @@ -186,7 +186,7 @@ func TestRealAdb(t *testing.T) { t.Fatalf("EnsureServer = %v, %v", ok, err) } t.Logf("EnsureServer took %v", time.Since(start)) - if n := logs.count(fmt.Sprintf("启动魔改 AVD 私有 adb server(端口 %d)", testServerPort)); n != 1 { + if n := logs.count(fmt.Sprintf("启动 ModAVD 私有 adb server(端口 %d)", testServerPort)); n != 1 { t.Errorf("start log lines = %d", n) } pid, up, err := win.ListenerPID(testServerPort) @@ -205,7 +205,7 @@ func TestRealAdb(t *testing.T) { if err != nil || !ok { t.Errorf("second EnsureServer = %v, %v", ok, err) } - if n := logs.count("启动魔改 AVD 私有 adb server"); n != 1 { + if n := logs.count("启动 ModAVD 私有 adb server"); n != 1 { t.Errorf("second EnsureServer started again (%d log lines)", n) } // PrepareForClose with the server up asks it first; it does not know emulator-20080, and no qemu of this @@ -277,7 +277,7 @@ func TestRealAdb(t *testing.T) { if err != nil || !ok { t.Fatalf("EnsureScriptServer = %v, %v", ok, err) } - if n := logs.count(fmt.Sprintf("启动魔改 AVD 脚本专用 adb server(端口 %d)", testScriptPort)); n != 1 { + if n := logs.count(fmt.Sprintf("启动 ModAVD 脚本专用 adb server(端口 %d)", testScriptPort)); n != 1 { t.Errorf("script start log lines = %d", n) } pid, _, _ := win.ListenerPID(testScriptPort) diff --git a/internal/adb/serverlog.go b/internal/adb/serverlog.go index 7f4da0f..41a05b6 100644 --- a/internal/adb/serverlog.go +++ b/internal/adb/serverlog.go @@ -139,7 +139,7 @@ func (c *Client) reportGoneServer(k serverKind, port int) { if found && (s.created == 0 || p.Created == s.created) { what = "还在,但不再监听" } - c.Log.Warnf("魔改 AVD %s adb server(端口 %d,pid %d,%s 起来的)%s%s", k.label, port, s.pid, + c.Log.Warnf("ModAVD %s adb server(端口 %d,pid %d,%s 起来的)%s%s", k.label, port, s.pid, s.at.Format("15:04:05"), what, logTailText(ServerLogTail(c.Root))) } @@ -159,7 +159,7 @@ func (c *Client) reportServerLog(k serverKind, port int, timeout time.Duration) if same { return } - c.Log.Warnf("魔改 AVD %s adb server(端口 %d)%.0f 秒内没监听上%s", k.label, port, timeout.Seconds(), + c.Log.Warnf("ModAVD %s adb server(端口 %d)%.0f 秒内没监听上%s", k.label, port, timeout.Seconds(), logTailText(lines)) } diff --git a/internal/avd/avd.go b/internal/avd/avd.go index 4924829..a590af2 100644 --- a/internal/avd/avd.go +++ b/internal/avd/avd.go @@ -426,7 +426,7 @@ func Get(r *root.Root, i int) (*Instance, error) { return nil, notFound(i) } -func notFound(i int) error { return errs.Newf(errs.NotFound, "魔改 AVD 实例 %d 不存在", i) } +func notFound(i int) error { return errs.Newf(errs.NotFound, "ModAVD 实例 %d 不存在", i) } // lockInstance takes root.LockInstanceConfig of i. noDir: the avd directory does not exist (no lock taken, nothing on // disk to guard); err is nil then. @@ -509,10 +509,10 @@ func ValidateOptions(memoryMB, cpu, dataGB *int) (int, int, int, error) { data = *dataGB } if !slices.Contains(MemoryChoicesMB, memory) { - return 0, 0, 0, errs.New(errs.InvalidArgument, "魔改 AVD 的内存只能选 "+joinInts(MemoryChoicesMB, "%d GB", 1024)) + return 0, 0, 0, errs.New(errs.InvalidArgument, "ModAVD 的内存只能选 "+joinInts(MemoryChoicesMB, "%d GB", 1024)) } if !slices.Contains(CPUChoices, cores) { - return 0, 0, 0, errs.New(errs.InvalidArgument, "魔改 AVD 的 CPU 核数只能选 "+joinInts(CPUChoices, "%d", 1)) + return 0, 0, 0, errs.New(errs.InvalidArgument, "ModAVD 的 CPU 核数只能选 "+joinInts(CPUChoices, "%d", 1)) } if data < MinDataPartitionGB || data > MaxDataPartitionGB { return 0, 0, 0, errs.Newf(errs.InvalidArgument, "数据盘上限只能在 %d–%d GB 之间", MinDataPartitionGB, @@ -528,7 +528,7 @@ func ValidateFPS(fps *int) (int, error) { value = *fps } if !slices.Contains(FPSChoices, value) { - return 0, errs.New(errs.InvalidArgument, "魔改 AVD 的帧率只能选 "+joinInts(FPSChoices, "%d", 1)) + return 0, errs.New(errs.InvalidArgument, "ModAVD 的帧率只能选 "+joinInts(FPSChoices, "%d", 1)) } return value, nil } @@ -714,7 +714,7 @@ func pathExists(path string) bool { } // FreeIndex is the smallest valid index that has no instance and whose three ports in plan busy() reports free. -// errs.Busy "魔改 AVD 实例已满(端口段 20000–20100 最多 9 台)" (the root's plan) when there is none. A nil busy skips +// errs.Busy "ModAVD 实例已满(端口段 20000–20100 最多 9 台)" (the root's plan) when there is none. A nil busy skips // the port check. func FreeIndex(plan root.PortPlan, existing map[int]bool, busy func(ports []int) []int) (int, error) { for i := 0; i <= root.MaxIndex; i++ { @@ -727,7 +727,7 @@ func FreeIndex(plan root.PortPlan, existing map[int]bool, busy func(ports []int) } return i, nil } - return 0, errs.Newf(errs.Busy, "魔改 AVD 实例已满(端口段 %d–%d 最多 9 台)", plan.Base, plan.Base+100) + return 0, errs.Newf(errs.Busy, "ModAVD 实例已满(端口段 %d–%d 最多 9 台)", plan.Base, plan.Base+100) } type UpdateOptions struct { diff --git a/internal/avd/avd_test.go b/internal/avd/avd_test.go index c4cffdb..d0d3360 100644 --- a/internal/avd/avd_test.go +++ b/internal/avd/avd_test.go @@ -635,7 +635,7 @@ func TestListAndGet(t *testing.T) { os.Remove(r.AvdHome()) _, err := Get(r, 0) wantCode(t, err, errs.NotFound) - if err.Error() != "魔改 AVD 实例 0 不存在" { + if err.Error() != "ModAVD 实例 0 不存在" { t.Fatalf("message: %v", err) } for _, i := range []int{4, 0, 9} { @@ -727,9 +727,9 @@ func TestValidate(t *testing.T) { memory, cpu, data *int msg string }{ - {ip(1000), nil, nil, "魔改 AVD 的内存只能选 3 GB / 4 GB / 5 GB / 6 GB"}, - {ip(0), ip(3), ip(1), "魔改 AVD 的内存只能选 3 GB / 4 GB / 5 GB / 6 GB"}, - {nil, ip(3), ip(1), "魔改 AVD 的 CPU 核数只能选 2 / 4 / 6"}, + {ip(1000), nil, nil, "ModAVD 的内存只能选 3 GB / 4 GB / 5 GB / 6 GB"}, + {ip(0), ip(3), ip(1), "ModAVD 的内存只能选 3 GB / 4 GB / 5 GB / 6 GB"}, + {nil, ip(3), ip(1), "ModAVD 的 CPU 核数只能选 2 / 4 / 6"}, {nil, nil, ip(15), "数据盘上限只能在 16–512 GB 之间"}, {nil, nil, ip(513), "数据盘上限只能在 16–512 GB 之间"}, } { @@ -753,7 +753,7 @@ func TestValidate(t *testing.T) { } _, err = ValidateFPS(ip(0)) wantCode(t, err, errs.InvalidArgument) - if err.Error() != "魔改 AVD 的帧率只能选 30 / 60" { + if err.Error() != "ModAVD 的帧率只能选 30 / 60" { t.Errorf("fps message %q", err.Error()) } } @@ -805,7 +805,7 @@ func TestFreeIndex(t *testing.T) { } _, err = FreeIndex(def, all, func(ports []int) []int { return ports }) wantCode(t, err, errs.Busy) - if err.Error() != "魔改 AVD 实例已满(端口段 20000–20100 最多 9 台)" { + if err.Error() != "ModAVD 实例已满(端口段 20000–20100 最多 9 台)" { t.Fatalf("message %v", err) } // another plan: the ports asked about and the message are the plan's @@ -815,7 +815,7 @@ func TestFreeIndex(t *testing.T) { t.Fatalf("plan 21000: FreeIndex = %d %v, asked %v", i, err, asked) } _, err = FreeIndex(root.PortPlan{Base: 21000}, all, func(ports []int) []int { return ports }) - if err == nil || err.Error() != "魔改 AVD 实例已满(端口段 21000–21100 最多 9 台)" { + if err == nil || err.Error() != "ModAVD 实例已满(端口段 21000–21100 最多 9 台)" { t.Fatalf("plan 21000: message %v", err) } all[7] = true diff --git a/internal/avd/testdata/instances_ref.py b/internal/avd/testdata/instances_ref.py index adcebfc..f49f1e4 100644 --- a/internal/avd/testdata/instances_ref.py +++ b/internal/avd/testdata/instances_ref.py @@ -328,12 +328,12 @@ def validate_options(memory_mb, cpu, data_partition_gb): ) if memory not in MEMORY_CHOICES_MB: raise ValueError( - "魔改 AVD 的内存只能选 " + "ModAVD 的内存只能选 " + " / ".join(f"{value // 1024} GB" for value in MEMORY_CHOICES_MB) ) if cores not in CPU_CHOICES: raise ValueError( - "魔改 AVD 的 CPU 核数只能选 " + " / ".join(str(v) for v in CPU_CHOICES) + "ModAVD 的 CPU 核数只能选 " + " / ".join(str(v) for v in CPU_CHOICES) ) low, high = DATA_PARTITION_RANGE_GB if not low <= data_gb <= high: @@ -345,7 +345,7 @@ def validate_fps(fps): value = DEFAULT_FPS if fps is None else int(fps) if value not in FPS_CHOICES: raise ValueError( - "魔改 AVD 的帧率只能选 " + " / ".join(str(v) for v in FPS_CHOICES) + "ModAVD 的帧率只能选 " + " / ".join(str(v) for v in FPS_CHOICES) ) return value @@ -432,7 +432,7 @@ def write_instance_settings(root, idx, changes): """AvdManager.write_instance_settings for cpu / memoryMb / fps (no display keys, no expected values).""" instance = list_instances(root).get(str(idx)) if instance is None: - raise RuntimeError(f"魔改 AVD 实例 {idx} 不存在") + raise RuntimeError(f"ModAVD 实例 {idx} 不存在") fps = validate_fps(changes["fps"]) if "fps" in changes else None memory, cpu, _ = validate_options( changes.get("memoryMb", memory_for(instance_meta(root, idx))), diff --git a/internal/cli/commands.go b/internal/cli/commands.go index c1c25ed..22ec6a6 100644 --- a/internal/cli/commands.go +++ b/internal/cli/commands.go @@ -124,7 +124,7 @@ func init() { "status 列出这五个文件在不在、从哪来(download / import),不把它算进必需组件。" + "不带参数时默认查看组件;source 查看或修改记住的下载源(auto、googledownloads_cn、tencent、google)。" + "--json 时必须给 --accept-license(仅 fetch)。只有 Windows 有组件清单,darwin、linux 返回 unsupported。" + - "退出码:没同意许可协议是 2(license_declined),路径有中文、空间不够、不是魔改 AVD 的文件夹是 4," + + "退出码:没同意许可协议是 2(license_declined),路径有中文、空间不够、不是 ModAVD 的文件夹是 4," + "正在补全是 5,下不成是 9(components_unavailable),下载或解压后没通过校验、导入的文件被拒收也是 9(components_rejected," + "--json 的 error.details.reason 是 unknown_file、mismatch 或 incomplete)。" if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { @@ -236,7 +236,7 @@ func init() { { name: "collect-info", args: "[--out-dir 目录] [--stage-dir 目录] [--keep-stage] [--no-explorer]", summary: "收集问题信息,打成 zip", - details: "打成「魔改AVD问题信息-<时间>.zip」,默认放桌面,再打开资源管理器选中它(--no-explorer 不打开)。" + + details: "打成「ModAVD-问题信息-<时间>.zip」,默认放桌面,再打开资源管理器选中它(--no-explorer 不打开)。" + "--stage-dir 是暂存目录的父目录(默认 %TEMP%),--keep-stage 保留暂存目录。每一节单独收,出错写进 summary.txt 接着收下一节。" + "只读:不收磁盘镜像,不收 AUTO-MAS 的配置和账号数据,不起 adb server、不碰 5037。目前只支持 Windows。", flags: []flagSpec{optOutDir, optStageDir, optKeepStage, optNoExplorer}, final: true, @@ -805,7 +805,7 @@ func stopAll(c *cmdCtx, m manager, opt emu.StopOptions) (*StopAllResult, error) c.log.Infof("没有正在运行的实例") return res, nil } - c.log.Infof("同时正常关闭 %d 台魔改 AVD 实例(安卓正常关机):%s", len(targets), joinInts(targets, "、")) + c.log.Infof("同时正常关闭 %d 台 ModAVD 实例(安卓正常关机):%s", len(targets), joinInts(targets, "、")) type outcome struct { r *emu.StopResult err error @@ -830,7 +830,7 @@ func stopAll(c *cmdCtx, m manager, opt emu.StopOptions) (*StopAllResult, error) if c.ctx.Err() != nil && errs.CodeOf(e) != errs.Canceled { e = errs.Wrap(errs.Canceled, e, "已取消") } - c.log.Warnf("魔改 AVD 实例 %d 关机失败: %s", targets[k], errs.As(e).Error()) + c.log.Warnf("ModAVD 实例 %d 关机失败: %s", targets[k], errs.As(e).Error()) res.Failed = append(res.Failed, StopFailure{Index: targets[k], Error: errorInfo(e)}) continue } @@ -933,7 +933,7 @@ func humanCollectInfo(w io.Writer, data any) { fmt.Fprintln(w, l) } if !r.MasFound { - fmt.Fprintln(w, "没找到 AUTO-MAS 安装目录,反馈时请附上 AUTO-MAS 的 debug 文件夹里的日志") + fmt.Fprintln(w, report.MasMissingNote) } if len(r.Problems) > 0 { fmt.Fprintf(w, "有 %d 节没收集成功,原因写在 zip 里的 summary.txt,照样提交就行。\n", len(r.Problems)) diff --git a/internal/cli/components_test.go b/internal/cli/components_test.go index 050aa35..4725106 100644 --- a/internal/cli/components_test.go +++ b/internal/cli/components_test.go @@ -186,7 +186,7 @@ func TestComponentsFetchOptions(t *testing.T) { t.Fatalf("quiet: %q", res.stderr) } if !strings.Contains(res.stdout, "已装好:平台工具(adb)(版本 37.0.1)") || !strings.Contains(res.stdout, "原来不完整的目录改名为") || - !strings.Contains(res.stdout, "完成,可以在 AUTO-MAS 里添加这个文件夹了。") { + !strings.Contains(res.stdout, "完成,可以新建实例了;用 AUTO-MAS 的,也可以在 AUTO-MAS 里添加这个文件夹了。") { t.Fatalf("summary %q", res.stdout) } } diff --git a/internal/cli/format.go b/internal/cli/format.go index 6d9914a..1bf4b69 100644 --- a/internal/cli/format.go +++ b/internal/cli/format.go @@ -602,7 +602,7 @@ func formatComponents(w io.Writer, data any) { if !fetched { fmt.Fprint(w, "组件都已齐全,不需要下载。") } - fmt.Fprintln(w, "完成,可以在 AUTO-MAS 里添加这个文件夹了。") + fmt.Fprintln(w, "完成,可以新建实例了;用 AUTO-MAS 的,也可以在 AUTO-MAS 里添加这个文件夹了。") } } @@ -801,7 +801,7 @@ func formatHelp(w io.Writer, r *HelpResult) { } return } - fmt.Fprintln(w, "modavd:魔改 AVD 实例管理(新建、删除、修改、列出、开机、关机)和 MCP 服务") + fmt.Fprintln(w, "modavd:ModAVD 实例管理(新建、删除、修改、列出、开机、关机)和 MCP 服务") fmt.Fprintf(w, "\n用法:%s\n\n命令:\n", r.Usage) // Usage and summary on separate lines: create's usage alone is ~80 columns. for _, e := range r.Commands { diff --git a/internal/cli/report_test.go b/internal/cli/report_test.go index 603c587..7e07542 100644 --- a/internal/cli/report_test.go +++ b/internal/cli/report_test.go @@ -54,8 +54,12 @@ func TestHumanCollectInfoMatchesScript(t *testing.T) { t.Fatalf("%s: the script's privacy note head not found once", c.name) } want = strings.Replace(want, scriptPrivacyHead+"\n", head+"\n", 1) + // and for what it says differently on purpose (report's reworded) + want = strings.Replace(want, "魔改AVD问题信息-", "ModAVD-问题信息-", 1) + want = strings.Replace(want, "实例配置、AUTO-MAS 日志(mas 文件夹);", "实例配置;找到 AUTO-MAS 时还有它的日志(mas 文件夹);", 1) + want = strings.Replace(want, "没找到 AUTO-MAS 安装目录,反馈时请附上 AUTO-MAS 的 debug 文件夹里的日志\n", report.MasMissingNote+"\n", 1) res := &report.CollectResult{ - ZipPath: `{{BASE}}\out\魔改AVD问题信息-20260115-120000.zip`, ZipSize: "{{SIZE}}", + ZipPath: `{{BASE}}\out\ModAVD-问题信息-20260115-120000.zip`, ZipSize: "{{SIZE}}", PrivacyNote: report.PrivacyNote, MasFound: c.mas, Problems: c.problems, } var b bytes.Buffer diff --git a/internal/components/fetch.go b/internal/components/fetch.go index c6f9f63..d932f59 100644 --- a/internal/components/fetch.go +++ b/internal/components/fetch.go @@ -334,7 +334,7 @@ func Fetch(ctx context.Context, r *root.Root, o Options) (*Result, error) { return nil, errs.New(errs.Unsupported, "这个系统还没有组件清单").WithAdvice("目前支持 Windows 和 Apple Silicon macOS") } if r == nil { - return nil, errs.New(errs.NotFound, "找不到魔改 AVD 根目录") + return nil, errs.New(errs.NotFound, "找不到 ModAVD 根目录") } if err := o.CheckTimeouts(); err != nil { return nil, err @@ -389,7 +389,7 @@ func (f *fetcher) run(ctx context.Context) (res *Result, err error) { if f.o.PlayStore { what += "、Google Play 商店" } - f.say("魔改 AVD:补全 Google 组件(" + what + ")") + f.say("ModAVD:补全 Google 组件(" + what + ")") f.say("文件夹:" + r.Dir) free := "-1" if f.o.TestFreeSpaceMB != nil { @@ -413,7 +413,7 @@ func (f *fetcher) run(ctx context.Context) (res *Result, err error) { if f.darwin() { advice = "请完整解压 Apple Silicon 发行包,再在包根目录运行 modavd components fetch" } - return nil, errs.Newf(errs.NotReady, "这里没有 %s,不像是魔改 AVD 的文件夹:%s", filepath.Join("sdk", "emulator", root.CurrentPlatform().Emulator), r.Dir). + return nil, errs.Newf(errs.NotReady, "这里没有 %s,不像是 ModAVD 的文件夹:%s", filepath.Join("sdk", "emulator", root.CurrentPlatform().Emulator), r.Dir). WithAdvice(advice) } if r.LocalSDKRoot() != "" { @@ -459,7 +459,7 @@ func (f *fetcher) run(ctx context.Context) (res *Result, err error) { if f.darwin() { f.say("组件都已齐全,不需要下载。创建或启动前仍需通过 HVF、镜像与模拟器版本检查。") } else { - f.say("组件都已齐全,不需要下载。完成,可以在 AUTO-MAS 里添加这个文件夹了。") + f.say("组件都已齐全,不需要下载。完成,可以新建实例了;用 AUTO-MAS 的,也可以在 AUTO-MAS 里添加这个文件夹了。") } return res, nil } @@ -534,7 +534,7 @@ func (f *fetcher) run(ctx context.Context) (res *Result, err error) { if f.darwin() { f.say("组件补全完成。创建或启动前仍需通过 HVF、镜像与模拟器版本检查。") } else { - f.say("完成,可以在 AUTO-MAS 里添加这个文件夹了。") + f.say("完成,可以新建实例了;用 AUTO-MAS 的,也可以在 AUTO-MAS 里添加这个文件夹了。") } return res, nil } diff --git a/internal/components/fetch_windows_test.go b/internal/components/fetch_windows_test.go index 464166a..f994c5a 100644 --- a/internal/components/fetch_windows_test.go +++ b/internal/components/fetch_windows_test.go @@ -472,7 +472,7 @@ func TestFetchInstallsThenSkips(t *testing.T) { t.Errorf("the download source line is not before the license:\n%s", logged) } for _, s := range []string{"参数:AcceptLicense=true", " URL " + m.url() + "pt.zip", "下载结束:", "退出码 0", - "完成,可以在 AUTO-MAS 里添加这个文件夹了。"} { + "完成,可以新建实例了;用 AUTO-MAS 的,也可以在 AUTO-MAS 里添加这个文件夹了。"} { if !strings.Contains(logged, s) { t.Errorf("log lacks %q:\n%s", s, logged) } diff --git a/internal/components/playstore.go b/internal/components/playstore.go index d5d5b4e..38fae24 100644 --- a/internal/components/playstore.go +++ b/internal/components/playstore.go @@ -184,7 +184,7 @@ func ImportPlayStore(ctx context.Context, r *root.Root, files []ImportFile, o Op return nil, errs.New(errs.Unsupported, "这个系统还没有组件清单").WithAdvice("目前支持 Windows 和 Apple Silicon macOS") } if r == nil { - return nil, errs.New(errs.NotFound, "找不到魔改 AVD 根目录") + return nil, errs.New(errs.NotFound, "找不到 ModAVD 根目录") } if len(files) == 0 { return nil, errs.New(errs.Usage, "没有给要导入的文件"). @@ -197,7 +197,7 @@ func ImportPlayStore(ctx context.Context, r *root.Root, files []ImportFile, o Op f := &fetcher{r: r, o: o, sdk: r.SDK()} emulator := filepath.Join("sdk", "emulator", root.CurrentPlatform().Emulator) if _, err := os.Stat(filepath.Join(f.sdk, "emulator", root.CurrentPlatform().Emulator)); err != nil { - return nil, errs.Newf(errs.NotReady, "这里没有 %s,不像是魔改 AVD 的文件夹:%s", emulator, r.Dir) + return nil, errs.Newf(errs.NotReady, "这里没有 %s,不像是 ModAVD 的文件夹:%s", emulator, r.Dir) } if r.LocalSDKRoot() != "" { return nil, errs.New(errs.NotReady, "这个根目录在 mas-avd.json 里设了 sdkRoot(开发用的本地 SDK),不导入组件"). @@ -230,7 +230,7 @@ func ImportPlayStore(ctx context.Context, r *root.Root, files []ImportFile, o Op } f.note(fmt.Sprintf("退出码 %d", errs.ExitCode(err))) }() - f.say("魔改 AVD:用自己的文件补全 Google Play 商店") + f.say("ModAVD:用自己的文件补全 Google Play 商店") f.say("文件夹:" + r.Dir) if f.darwin() { if err := f.platformPaths(); err != nil { diff --git a/internal/emu/adbcmd.go b/internal/emu/adbcmd.go index f4f1650..dc57038 100644 --- a/internal/emu/adbcmd.go +++ b/internal/emu/adbcmd.go @@ -29,7 +29,7 @@ func (m *Manager) Shell(ctx context.Context, i int, command string, timeout time if _, ok, err := m.qemuOf(i); err != nil { return nil, err } else if !ok { - return nil, errs.Newf(errs.Unsupported, "魔改 AVD 实例 %d 没有在运行", i) + return nil, errs.Newf(errs.Unsupported, "ModAVD 实例 %d 没有在运行", i) } res := m.shell(ctx, i, command, timeout) if err := ctxErr(ctx); err != nil { diff --git a/internal/emu/bootadb_windows_test.go b/internal/emu/bootadb_windows_test.go index 70746ae..5662120 100644 --- a/internal/emu/bootadb_windows_test.go +++ b/internal/emu/bootadb_windows_test.go @@ -42,7 +42,7 @@ func TestWaitBootAdbUnusableAfterBoot(t *testing.T) { adbAfterBootWait, bootRegisterEvery = 1500*time.Millisecond, 500*time.Millisecond t0 := time.Now() _, err := m.waitBoot(context.Background(), 3, time.Hour, &q, nil, "", &progressor{}, nil, nil) - if errs.CodeOf(err) != errs.Timeout || !strings.Contains(err.Error(), "魔改 AVD 实例 3 的安卓已经开好了(模拟器报告开机完成),但之后") || + if errs.CodeOf(err) != errs.Timeout || !strings.Contains(err.Error(), "ModAVD 实例 3 的安卓已经开好了(模拟器报告开机完成),但之后") || !strings.Contains(err.Error(), "私有 adb(端口 20391)一直用不了") { t.Fatalf("err %v", err) } diff --git a/internal/emu/bootdiag_windows_test.go b/internal/emu/bootdiag_windows_test.go index b052e6b..606d11c 100644 --- a/internal/emu/bootdiag_windows_test.go +++ b/internal/emu/bootdiag_windows_test.go @@ -35,7 +35,7 @@ func TestSaveBootFailure(t *testing.T) { return m.saveBootFailure(ctx, i, err, running) } - failure := errs.New(errs.Timeout, "魔改 AVD 实例 6 启动超时") + failure := errs.New(errs.Timeout, "ModAVD 实例 6 启动超时") diagDirs := func() []string { matches, _ := filepath.Glob(filepath.Join(r.LogsDir(), "mas_6-*-bootfail")) return matches diff --git a/internal/emu/bridge.go b/internal/emu/bridge.go index 331c7ad..30dc23b 100644 --- a/internal/emu/bridge.go +++ b/internal/emu/bridge.go @@ -359,7 +359,7 @@ func (m *Manager) installBridge(ctx context.Context, i int, opt StartOptions, re // The script's notes go to this log; the warnings of the cold boots come with their results (m, not mm, starts them). col := &logx.Collector{} mm := m.withLog(col.Sink()) - mm.Log.Infof("魔改 AVD 实例 %d 有执行桥的开机标记,桥还没装:现在装(中间要关机再开机一到两次)", i) + mm.Log.Infof("ModAVD 实例 %d 有执行桥的开机标记,桥还没装:现在装(中间要关机再开机一到两次)", i) notWritable := 0 for round := 1; round <= bridgeInstallRounds; round++ { if opt.Progress != nil { @@ -455,7 +455,7 @@ func (m *Manager) updateBridgeLocked(ctx context.Context, i int, opt StartOption res.Warnings = append(res.Warnings, col.Messages()...) return res, nil } - mm.Log.Infof("魔改 AVD 实例 %d 的执行桥文件和根目录里的载荷不一样(多半是更新过 ModAVD 包):现在更新,之后冷启动一次", i) + mm.Log.Infof("ModAVD 实例 %d 的执行桥文件和根目录里的载荷不一样(多半是更新过 ModAVD 包):现在更新,之后冷启动一次", i) if opt.Progress != nil { opt.Progress(Progress{Step: "bridge", Message: fmt.Sprintf("实例 %d 更新 arm64 执行桥", i), Done: 0, Total: progressTotal}) @@ -603,7 +603,7 @@ func (m *Manager) Bridge(ctx context.Context, i int, action string) (*BridgeResu return nil, err } if !ok { - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正在装或卸执行桥,稍后再试", i), map[string]string{errs.Reason: "locked", errs.Lock: "bridge"}) + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正在装或卸执行桥,稍后再试", i), map[string]string{errs.Reason: "locked", errs.Lock: "bridge"}) } defer bridgeLock.Unlock() res := &BridgeResult{Index: i, Action: action, ExitCode: -1, Warnings: []string{}} diff --git a/internal/emu/check.go b/internal/emu/check.go index 908a546..03d8bf1 100644 --- a/internal/emu/check.go +++ b/internal/emu/check.go @@ -39,7 +39,7 @@ const ( "「Windows 虚拟机监控程序平台」,然后重启" vulkanAdvice = "安装或更新显卡驱动(NVIDIA / AMD / Intel 官网驱动自带 Vulkan)。没有可用的显卡时模拟器只能" + "软件渲染,游戏会占满 CPU,性能会很差" - testPackageAdvice = `请把魔改 AVD 内测包解压到这个目录(解压后目录里应有 sdk\emulator)` + testPackageAdvice = `请把 ModAVD 内测包解压到这个目录(解压后目录里应有 sdk\emulator)` // How long a result of the same check is reused within one process (MCP server): AUTO-MAS // _ACCEL_CACHE_SECONDS, VULKAN_PROBE_CACHE_SECONDS, VULKAN_PROBE_TIMEOUT_CACHE_SECONDS. @@ -132,7 +132,7 @@ func (m *Manager) precheckBeforeLaunch(ctx context.Context, memoryMB int) error return err } if vulkan.OK != nil && !*vulkan.OK { - m.Log.Warnf("魔改 AVD 开机前检查:%s", itemMessage(vulkan)) + m.Log.Warnf("ModAVD 开机前检查:%s", itemMessage(vulkan)) } items = append(items, vulkan) m.Log.Infof("%s", precheckSummary(items)) @@ -179,7 +179,7 @@ func precheckError(it CheckItem, missingErr func() error) error { } } -// precheckSummary is AUTO-MAS's one log line: "魔改 AVD 开机前检查:目录路径通过(<根目录>);组件通过(...);...". +// precheckSummary is AUTO-MAS's one log line: "ModAVD 开机前检查:目录路径通过(<根目录>);组件通过(...);...". func precheckSummary(items []CheckItem) string { parts := make([]string, len(items)) for k, it := range items { @@ -192,7 +192,7 @@ func precheckSummary(items []CheckItem) string { } parts[k] = fmt.Sprintf("%s%s(%s)", it.Title, verdict, it.Reason) } - return "魔改 AVD 开机前检查:" + strings.Join(parts, ";") + return "ModAVD 开机前检查:" + strings.Join(parts, ";") } func pathIsASCII(s string) bool { @@ -324,7 +324,7 @@ func emulatorItem(f emulatorFacts) CheckItem { if !f.Present { return CheckItem{ ID: "emulator", Title: title, OK: checkOK(false), Blocking: true, - Reason: "这个目录里还没有魔改 AVD 内测包", Advice: platformPackageAdvice(), + Reason: "这个目录里还没有 ModAVD 内测包", Advice: platformPackageAdvice(), } } version := f.Version @@ -334,7 +334,7 @@ func emulatorItem(f emulatorFacts) CheckItem { if !f.SelfBuilt { return CheckItem{ ID: "emulator", Title: title, OK: checkOK(false), Blocking: true, - Reason: fmt.Sprintf("这里的模拟器(%s)不是魔改 AVD 内测包里的版本,只支持内测包", version), + Reason: fmt.Sprintf("这里的模拟器(%s)不是 ModAVD 内测包里的版本,只支持内测包", version), Advice: platformPackageAdvice(), } } @@ -351,7 +351,7 @@ func emulatorItem(f emulatorFacts) CheckItem { return CheckItem{ ID: "emulator", Title: title, OK: checkOK(false), Blocking: true, Reason: fmt.Sprintf("内测包版本过旧(%s)", shown), - Advice: fmt.Sprintf("请把魔改 AVD 更新到 0.%d.0 或更新的版本,实例和游戏数据不受影响", root.MinModBuild), + Advice: fmt.Sprintf("请把 ModAVD 更新到 0.%d.0 或更新的版本,实例和游戏数据不受影响", root.MinModBuild), } } @@ -390,7 +390,7 @@ func diskItem(drive string, free uint64, err error) CheckItem { return CheckItem{ ID: "disk", Title: "磁盘", OK: checkOK(false), Blocking: true, Reason: fmt.Sprintf("%s,低于开机所需的 %d GB:数据盘随游戏写入增长,盘写满时实例数据会损坏", text, minFreeDiskGBToBoot), - Advice: fmt.Sprintf("清理 %s 的空间,或把魔改 AVD 根目录放到空间更大的盘", drive), + Advice: fmt.Sprintf("清理 %s 的空间,或把 ModAVD 根目录放到空间更大的盘", drive), } } @@ -460,11 +460,11 @@ func accelItem(res accelResult, summary string) CheckItem { backend, advice := "Windows 虚拟机监控程序平台", accelAdvice if runtime.GOOS == "darwin" { backend = "Hypervisor.framework" - advice = "请在 Apple Silicon Mac 本机运行 macos-arm64 版魔改 AVD,确认模拟器带 com.apple.security.hypervisor 签名权限;用 emulator -accel-check 检查 HVF" + advice = "请在 Apple Silicon Mac 本机运行 macos-arm64 版 ModAVD,确认模拟器带 com.apple.security.hypervisor 签名权限;用 emulator -accel-check 检查 HVF" } return CheckItem{ ID: "acceleration", Title: "硬件虚拟化", OK: checkOK(false), Blocking: true, - Reason: fmt.Sprintf("硬件虚拟化(%s)不可用,魔改 AVD 无法启动(检查结果:%s)", backend, summary), + Reason: fmt.Sprintf("硬件虚拟化(%s)不可用,ModAVD 无法启动(检查结果:%s)", backend, summary), Advice: advice, } } @@ -578,7 +578,7 @@ func accelOKText(summary string) string { func platformPackageAdvice() string { if runtime.GOOS == "darwin" { - return "请把魔改 AVD 的 macos-arm64 内测包解压到这个目录(解压后应有 sdk/emulator)" + return "请把 ModAVD 的 macos-arm64 内测包解压到这个目录(解压后应有 sdk/emulator)" } return testPackageAdvice } diff --git a/internal/emu/check_darwin.go b/internal/emu/check_darwin.go index 1d4f7cb..b66c965 100644 --- a/internal/emu/check_darwin.go +++ b/internal/emu/check_darwin.go @@ -16,7 +16,7 @@ import ( // A directory named arm64-v8a must not silently make an x86 image compatible. func (m *Manager) platformCheckItems() []CheckItem { it := CheckItem{ID: "guest-architecture", Title: "客体架构", OK: checkOK(false), Blocking: true, - Advice: "请使用 macos-arm64 版魔改 AVD 和对应的 Apple Silicon 系统镜像"} + Advice: "请使用 macos-arm64 版 ModAVD 和对应的 Apple Silicon 系统镜像"} p := root.CurrentPlatform() if !p.Supported() || p.GuestABI != "arm64-v8a" || p.GuestArch != "arm64" { it.Reason = "当前 macOS 管理器仅支持 Apple Silicon 和 arm64-v8a 客体" diff --git a/internal/emu/check_test.go b/internal/emu/check_test.go index 08fedfb..aecfc02 100644 --- a/internal/emu/check_test.go +++ b/internal/emu/check_test.go @@ -131,11 +131,11 @@ func TestComponentsItem(t *testing.T) { func TestEmulatorItem(t *testing.T) { it := emulatorItem(emulatorFacts{}) - if it.OK == nil || *it.OK || it.Reason != "这个目录里还没有魔改 AVD 内测包" || it.Advice != testPackageAdvice { + if it.OK == nil || *it.OK || it.Reason != "这个目录里还没有 ModAVD 内测包" || it.Advice != testPackageAdvice { t.Fatalf("absent = %+v", it) } it = emulatorItem(emulatorFacts{Present: true, Version: "37.1.11"}) - if it.OK == nil || *it.OK || it.Reason != "这里的模拟器(37.1.11)不是魔改 AVD 内测包里的版本,只支持内测包" { + if it.OK == nil || *it.OK || it.Reason != "这里的模拟器(37.1.11)不是 ModAVD 内测包里的版本,只支持内测包" { t.Fatalf("google build = %+v", it) } it = emulatorItem(emulatorFacts{Present: true}) @@ -240,7 +240,7 @@ func TestPrecheckSummary(t *testing.T) { {Title: "硬件虚拟化", OK: nil, Reason: "没装"}, {Title: "显卡 Vulkan", OK: checkOK(false), Reason: "没有"}, }) - want := `魔改 AVD 开机前检查:目录路径通过(D:\r);硬件虚拟化未查(没装);显卡 Vulkan未通过(没有)` + want := `ModAVD 开机前检查:目录路径通过(D:\r);硬件虚拟化未查(没装);显卡 Vulkan未通过(没有)` if got != want { t.Fatalf("summary\n got %s\nwant %s", got, want) } diff --git a/internal/emu/createinit.go b/internal/emu/createinit.go index 097e83c..7976f55 100644 --- a/internal/emu/createinit.go +++ b/internal/emu/createinit.go @@ -112,7 +112,7 @@ func (m *Manager) notCreated(i int, what string) error { state, message, logFile := m.creation(i) switch state { case creationCreating: - return errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正在新建(后台开机做初始化),%s", i, what). + return errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正在新建(后台开机做初始化),%s", i, what). WithAdvice("等它做完;进度用 modavd status 查看"), map[string]string{errs.Reason: "locked", errs.Lock: "create"}) case creationFailed: advice := fmt.Sprintf("用 modavd create --resume %d 重新初始化,或者 modavd delete %d --yes 删掉", i, i) @@ -123,7 +123,7 @@ func (m *Manager) notCreated(i int, what string) error { message = "初始化没做完" } // the reason last: a setup error has its own brackets - return errs.Newf(errs.NotReady, "魔改 AVD 实例 %d 新建时初始化失败,%s:%s", i, what, message).WithAdvice(advice) + return errs.Newf(errs.NotReady, "ModAVD 实例 %d 新建时初始化失败,%s:%s", i, what, message).WithAdvice(advice) } return nil } @@ -425,7 +425,7 @@ func (m *Manager) setupNew(ctx context.Context, i int, cfg *avd.Ini, req CreateR if logFile != "" { advice += ";日志见 " + logFile } - return nil, &errs.Error{Code: e.Code, Message: fmt.Sprintf("魔改 AVD 实例 %d 新建失败:%s", i, e.Message), + return nil, &errs.Error{Code: e.Code, Message: fmt.Sprintf("ModAVD 实例 %d 新建失败:%s", i, e.Message), Advice: advice, Cause: err} } prog.stage("done", fmt.Sprintf("实例 %d 新建完成,已关机;第一次开机不用再做初始化", i)) @@ -450,7 +450,7 @@ func (m *Manager) stopCreateLeftover(ctx context.Context, i int) error { defer cancel() if err := createStop(m, sctx, i); err != nil { e := errs.As(err) - return errs.Wrap(e.Code, err, fmt.Sprintf("魔改 AVD 实例 %d 上次新建时开的模拟器没能正常关掉: %s", i, e.Message)). + return errs.Wrap(e.Code, err, fmt.Sprintf("ModAVD 实例 %d 上次新建时开的模拟器没能正常关掉: %s", i, e.Message)). WithAdvice(fmt.Sprintf("用 modavd stop %d 关掉它,再重试", i)) } return nil diff --git a/internal/emu/crud.go b/internal/emu/crud.go index eed5779..ebfca02 100644 --- a/internal/emu/crud.go +++ b/internal/emu/crud.go @@ -104,7 +104,7 @@ func (m *Manager) Create(ctx context.Context, req CreateRequest) (_ *InstanceInf lock.Unlock() return nil, err } - m.Log.Infof("已新建魔改 AVD 实例 %d(内存 %d MB / %d 核 / 数据盘 %d GB / 帧率 %d)", i, memory, cpu, dataGB, fps) + m.Log.Infof("已新建 ModAVD 实例 %d(内存 %d MB / %d 核 / 数据盘 %d GB / 帧率 %d)", i, memory, cpu, dataGB, fps) if req.NoBridge { // recorded, so no start installs it (bridge.go) if err := m.setBridgeWanted(i, false); err != nil { @@ -189,7 +189,7 @@ func (m *Manager) resumeCreate(ctx context.Context, req CreateRequest) (*Instanc // with the lock in hand, a recorded "creating" is one that was killed (creation() would see our own lock) if state := m.creationRecorded(i); state != creationFailed && state != creationCreating { lock.Unlock() - return nil, errs.Newf(errs.Unsupported, "魔改 AVD 实例 %d 不是新建失败的实例,没有要重做的初始化", i). + return nil, errs.Newf(errs.Unsupported, "ModAVD 实例 %d 不是新建失败的实例,没有要重做的初始化", i). WithAdvice(fmt.Sprintf("直接 modavd start %d", i)) } return m.setupNew(ctx, i, in.Config, req, lock) @@ -233,7 +233,7 @@ func (m *Manager) Update(ctx context.Context, i int, req UpdateRequest) (*Update warnings = col.Messages() applied["diagnostics"] = *req.Diagnostics } - m.Log.Infof("已写入魔改 AVD 实例 %d 的设置: %s", i, formatApplied(applied)) + m.Log.Infof("已写入 ModAVD 实例 %d 的设置: %s", i, formatApplied(applied)) in, err := m.instance(i) if err != nil { return nil, err @@ -246,7 +246,7 @@ func (m *Manager) Update(ctx context.Context, i int, req UpdateRequest) (*Update if ok { p = &q if nextBoot { - m.Log.Infof("魔改 AVD 实例 %d 正在运行,硬件改动下次开机生效", i) + m.Log.Infof("ModAVD 实例 %d 正在运行,硬件改动下次开机生效", i) } } return &UpdateResult{Instance: m.instanceInfo(ctx, in, p, nil), Applied: applied, AppliesNextBoot: p != nil && nextBoot, @@ -312,10 +312,10 @@ func (m *Manager) Delete(ctx context.Context, i int) (*DeleteResult, error) { return nil, err } else if running { unlock() - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 未关闭,无法删除", i), map[string]string{errs.Reason: "running"}) + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 未关闭,无法删除", i), map[string]string{errs.Reason: "running"}) } if len(leftovers) > 0 { - m.Log.Warnf("魔改 AVD 实例 %d 不完整(只剩:%s),可能是上次删除或新建到一半,把这些残留删掉", i, + m.Log.Warnf("ModAVD 实例 %d 不完整(只剩:%s),可能是上次删除或新建到一半,把这些残留删掉", i, strings.Join(leftovers, "、")) } if err := m.prepareDiagnosticDelete(ctx, i); err != nil { @@ -329,16 +329,16 @@ func (m *Manager) Delete(ctx context.Context, i int) (*DeleteResult, error) { // Confirm it is gone: a later create must not find a half-deleted instance under this index. if left := instanceFilesOnDisk(m.Root, i); len(left) > 0 { unlock() - return nil, errs.Newf(errs.Internal, "魔改 AVD 实例 %d 没有删干净,还剩:%s", i, strings.Join(left, "、")). + return nil, errs.Newf(errs.Internal, "ModAVD 实例 %d 没有删干净,还剩:%s", i, strings.Join(left, "、")). WithAdvice(fmt.Sprintf("关掉占用这些文件的程序后再运行一次 modavd delete %d --yes,会接着删", i)) } // The lock files can only go once nobody holds them (an open file cannot be deleted). unlock() m.removeLockFiles(i) if len(leftovers) > 0 { - m.Log.Infof("已清掉魔改 AVD 实例 %d 的残留", i) + m.Log.Infof("已清掉 ModAVD 实例 %d 的残留", i) } else { - m.Log.Infof("已删除魔改 AVD 实例 %d", i) + m.Log.Infof("已删除 ModAVD 实例 %d", i) } return &DeleteResult{Index: i, Name: in.Name, Path: in.Dir}, nil } @@ -393,14 +393,14 @@ func (m *Manager) prepareDiagnosticDelete(ctx context.Context, i int) error { return err } -// tryInstanceLock takes lock kind of instance i now or fails with errs.Busy ("魔改 AVD 实例 0 正在启动,无法删除"). +// tryInstanceLock takes lock kind of instance i now or fails with errs.Busy ("ModAVD 实例 0 正在启动,无法删除"). func (m *Manager) tryInstanceLock(i int, kind, what string) (*host.FileLock, error) { l, ok, err := m.tryOperationLock(i, kind) if err != nil { return nil, err } if !ok { - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d %s,无法删除", i, what), map[string]string{errs.Reason: "locked", errs.Lock: kind}) + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d %s,无法删除", i, what), map[string]string{errs.Reason: "locked", errs.Lock: kind}) } return l, nil } diff --git a/internal/emu/crud_test.go b/internal/emu/crud_test.go index 347a539..ced75cc 100644 --- a/internal/emu/crud_test.go +++ b/internal/emu/crud_test.go @@ -76,7 +76,7 @@ func TestDeleteHalfInstances(t *testing.T) { t.Fatalf("explicit create on leftovers: %v", err) } deleted("directory without config.ini", 0) - if !strings.Contains(strings.Join(logs.Messages(), "\n"), "魔改 AVD 实例 0 不完整(只剩:") { + if !strings.Contains(strings.Join(logs.Messages(), "\n"), "ModAVD 实例 0 不完整(只剩:") { t.Errorf("leftovers not reported: %q", logs.Messages()) } create(0) // the index is usable again diff --git a/internal/emu/disk.go b/internal/emu/disk.go index 59eb7a0..4e8511f 100644 --- a/internal/emu/disk.go +++ b/internal/emu/disk.go @@ -96,14 +96,14 @@ func (m *Manager) DiskTrim(ctx context.Context, i int) (*DiskTrimResult, error) return nil, err } else if !running { return nil, errs.Newf(errs.Unsupported, - "魔改 AVD 实例 %d 没有在运行,请先开机再回收磁盘空间", i) + "ModAVD 实例 %d 没有在运行,请先开机再回收磁盘空间", i) } if !m.bootCompleted(ctx, i, statusAdbTimeout, true) { if err := ctxErr(ctx); err != nil { return nil, err } return nil, errs.Newf(errs.Unsupported, - "魔改 AVD 实例 %d 还没开机完成,请开机后再回收磁盘空间", i) + "ModAVD 实例 %d 还没开机完成,请开机后再回收磁盘空间", i) } // Gate on the emulator actually advertising discard on /data: an old build @@ -115,7 +115,7 @@ func (m *Manager) DiskTrim(ctx context.Context, i int) (*DiskTrimResult, error) if !ok || maxBytes <= 0 { return nil, errs.Newf(errs.Unsupported, "这个模拟器版本不支持回收磁盘空间(客体数据盘没有 discard)"). - WithAdvice("请把魔改 AVD 更新到带磁盘回收的版本,实例和游戏数据不受影响") + WithAdvice("请把 ModAVD 更新到带磁盘回收的版本,实例和游戏数据不受影响") } // vold idle maintenance runs fstrim on the mounted file systems. (The exact @@ -463,9 +463,9 @@ func (m *Manager) lockInstanceForCompact(i int) (func(), error) { if err := m.compactBusy(i); err != nil { return nil, err } - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 的磁盘状态正在读取,请稍后再整理", i), map[string]string{errs.Reason: "locked", errs.Lock: "disk"}) + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 的磁盘状态正在读取,请稍后再整理", i), map[string]string{errs.Reason: "locked", errs.Lock: "disk"}) } - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d %s,无法整理磁盘", i, kw.what), map[string]string{errs.Reason: "locked", errs.Lock: kw.kind}) + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d %s,无法整理磁盘", i, kw.what), map[string]string{errs.Reason: "locked", errs.Lock: kw.kind}) } locks = append(locks, l) } @@ -714,7 +714,7 @@ func (m *Manager) DiskCompact(ctx context.Context, i int) (*DiskCompactResult, e if _, running, err := m.qemuOf(i); err != nil { return nil, err } else if running { - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 未关闭,无法整理磁盘", i). + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 未关闭,无法整理磁盘", i). WithAdvice(fmt.Sprintf("先 modavd stop %d,关机后再整理", i)), map[string]string{errs.Reason: "running"}) } if err := ctxErr(ctx); err != nil { @@ -770,7 +770,7 @@ func (m *Manager) DiskCompact(ctx context.Context, i int) (*DiskCompactResult, e return nil, err } if estimate != nil && *estimate < threshold { - m.Log.Infof("魔改 AVD 实例 %d 预计可回收 %d 字节,低于 %d 字节,跳过磁盘重写", i, *estimate, threshold) + m.Log.Infof("ModAVD 实例 %d 预计可回收 %d 字节,低于 %d 字节,跳过磁盘重写", i, *estimate, threshold) return compactResult(i, overlay, before, before, estimate, threshold, true), nil } // Without an allocation count, reserve for the complete virtual image and @@ -834,6 +834,6 @@ func (m *Manager) DiskCompact(ctx context.Context, i int) (*DiskCompactResult, e return nil, err } - m.Log.Infof("魔改 AVD 实例 %d 整理磁盘完成:占用 %s → %s", i, gib(before), gib(after)) + m.Log.Infof("ModAVD 实例 %d 整理磁盘完成:占用 %s → %s", i, gib(before), gib(after)) return compactResult(i, overlay, before, after, estimate, threshold, false), nil } diff --git a/internal/emu/disk_lock.go b/internal/emu/disk_lock.go index a3755f3..9240d5f 100644 --- a/internal/emu/disk_lock.go +++ b/internal/emu/disk_lock.go @@ -14,7 +14,7 @@ func (m *Manager) compactBusy(i int) error { return errs.Wrap(errs.Internal, err, "实例磁盘锁文件打不开: "+m.lockPath(i, "disk")) } if held { - return errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正在整理磁盘,请等整理结束后重试", i), map[string]string{errs.Reason: "locked", errs.Lock: "disk"}) + return errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正在整理磁盘,请等整理结束后重试", i), map[string]string{errs.Reason: "locked", errs.Lock: "disk"}) } return nil } diff --git a/internal/emu/firstboot.go b/internal/emu/firstboot.go index 988045b..c52211e 100644 --- a/internal/emu/firstboot.go +++ b/internal/emu/firstboot.go @@ -79,7 +79,7 @@ var debloatPackages = []string{ // Bluetooth off, no immersive-mode hint, phone apps disabled, the light launcher, the renderer. A failed item leaves // initialized=false and the next boot tries again. func (m *Manager) firstBootInit(ctx context.Context, i int) { - m.Log.Infof("魔改 AVD 实例 %d 首次开机,正在初始化", i) + m.Log.Infof("ModAVD 实例 %d 首次开机,正在初始化", i) var failures []string // Wi-Fi is not turned off here: every boot turns it on with the fastpath (setupNetwork) @@ -105,7 +105,7 @@ func (m *Manager) firstBootInit(ctx context.Context, i int) { } software := strings.Contains(strings.ToLower(renderer), "swiftshader") if software { - m.Log.Warnf("魔改 AVD 实例 %d 正在用软件渲染(%s):没有可用的显卡驱动,游戏会占满 CPU,请更新显卡驱动", i, renderer) + m.Log.Warnf("ModAVD 实例 %d 正在用软件渲染(%s):没有可用的显卡驱动,游戏会占满 CPU,请更新显卡驱动", i, renderer) } if ctx.Err() != nil { // the commands above were cut short: do not record a half run, the next boot does it again @@ -113,12 +113,12 @@ func (m *Manager) firstBootInit(ctx context.Context, i int) { } if err := m.writeInitMeta(i, len(failures) == 0, launcher, renderer, software, failures); err != nil { - m.Log.Warnf("魔改 AVD 实例 %d 初始化结果没写进 mas-avd.json,下次开机重做: %v", i, err) + m.Log.Warnf("ModAVD 实例 %d 初始化结果没写进 mas-avd.json,下次开机重做: %v", i, err) } if len(failures) > 0 { - m.Log.Warnf("魔改 AVD 实例 %d 初始化有失败项,下次开机重试: %s", i, pyList(failures)) + m.Log.Warnf("ModAVD 实例 %d 初始化有失败项,下次开机重试: %s", i, pyList(failures)) } else { - m.Log.Infof("魔改 AVD 实例 %d 初始化完成(桌面: %s)", i, launcher) + m.Log.Infof("ModAVD 实例 %d 初始化完成(桌面: %s)", i, launcher) } } diff --git a/internal/emu/install.go b/internal/emu/install.go index 71bfd73..eb7046a 100644 --- a/internal/emu/install.go +++ b/internal/emu/install.go @@ -59,10 +59,10 @@ func (m *Manager) InstallAPK(ctx context.Context, i int, apkPath string) (*Insta if err := ctxErr(ctx); err != nil { return nil, err } - return nil, errs.Newf(errs.Unsupported, "魔改 AVD 实例 %d 没有开机,请先开机再安装 APK", i) + return nil, errs.Newf(errs.Unsupported, "ModAVD 实例 %d 没有开机,请先开机再安装 APK", i) } - m.Log.Infof("魔改 AVD 实例 %d 安装 APK: %s", i, path) + m.Log.Infof("ModAVD 实例 %d 安装 APK: %s", i, path) res := m.ADB.Run(ctx, adb.RunOptions{Serial: m.Root.Ports().Serial(i), Timeout: apkInstallTimeout, Reregister: true}, "install", "-r", path) // The guest is done with the install: a waiting start may restart zygote now (reading the manifest below is local). @@ -72,14 +72,14 @@ func (m *Manager) InstallAPK(ctx context.Context, i int, apkPath string) (*Insta } last := installResultLine(res.Out) if res.Code != 0 || !strings.HasPrefix(last, "Success") { - m.Log.Warnf("魔改 AVD 实例 %d 安装 %s 失败: %s", i, name, res.Out) + m.Log.Warnf("ModAVD 实例 %d 安装 %s 失败: %s", i, name, res.Out) why := last if why == "" { why = "adb 返回 " + strconv.Itoa(res.Code) } return nil, errs.Newf(errs.Internal, "安装 %s 失败:%s", name, why) } - m.Log.Infof("魔改 AVD 实例 %d 已安装 %s", i, name) + m.Log.Infof("ModAVD 实例 %d 已安装 %s", i, name) out := &InstallResult{Index: i, APK: path, Output: last} if man, err := apk.ReadManifest(path); err == nil && man != nil { diff --git a/internal/emu/launch.go b/internal/emu/launch.go index bd69c6b..06eb92d 100644 --- a/internal/emu/launch.go +++ b/internal/emu/launch.go @@ -125,10 +125,10 @@ func (m *Manager) launchPlanFor(ctx context.Context, inst *avd.Instance, memoryM port := m.Root.Ports().Console(i) busy, err := host.BusyPorts([]int{port, port + 1, port + 2}) if err != nil { - return nil, errs.Wrap(errs.Internal, err, fmt.Sprintf("魔改 AVD 实例 %d 读不出系统的 TCP 监听表: %v", i, err)) + return nil, errs.Wrap(errs.Internal, err, fmt.Sprintf("ModAVD 实例 %d 读不出系统的 TCP 监听表: %v", i, err)) } if len(busy) > 0 { - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 要用的端口 %s 已被占用,请先关闭占用它们的程序", + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 要用的端口 %s 已被占用,请先关闭占用它们的程序", i, joinInts(busy, ", ")), map[string]string{errs.Reason: "port_in_use"}) } @@ -175,7 +175,7 @@ func (m *Manager) launchPlanFor(ctx context.Context, inst *avd.Instance, memoryM if root.CurrentPlatform().MirrorOS == "macos" { balloon = "" } - m.Log.Infof("启动魔改 AVD 实例 %d(%s,%dp %d Hz,%d MB / %s 核,%s不带声卡,ANDROID_EMU_VK_HIDE_REBAR=%s): emulator %s", + m.Log.Infof("启动 ModAVD 实例 %d(%s,%dp %d Hz,%d MB / %s 核,%s不带声卡,ANDROID_EMU_VK_HIDE_REBAR=%s): emulator %s", i, mode, inst.Resolution(), inst.FPS(), memoryMB, cpu, balloon, hideRebar, strings.Join(args, " ")) return &launchPlan{args: args, logPath: logPath, memoryMB: memoryMB}, nil } @@ -221,7 +221,7 @@ func otherRootQemu(q host.Process, cmdline []string, i, console int, ours func(h // otherRootBusy is the error of a start whose ports the same-numbered instance of another root holds. func otherRootBusy(i int, p host.Process, where string) *errs.Error { - return errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 的端口被另一个根目录的同编号实例占用(pid %d,%s)", i, p.PID, where). + return errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 的端口被另一个根目录的同编号实例占用(pid %d,%s)", i, p.PID, where). WithAdvice("先在那个根目录把它关掉再启动"), map[string]string{errs.Reason: "port_other_root"}) } @@ -281,7 +281,7 @@ func (m *Manager) startEmulator(i int, plan *launchPlan) (host.Process, error) { }) if err != nil { return host.Process{}, errs.Wrap(errs.LaunchFailed, err, - fmt.Sprintf("魔改 AVD 实例 %d 启动失败(%s 没能运行): %v", i, root.CurrentPlatform().Emulator, err)) + fmt.Sprintf("ModAVD 实例 %d 启动失败(%s 没能运行): %v", i, root.CurrentPlatform().Emulator, err)) } return p, nil } diff --git a/internal/emu/launch_test.go b/internal/emu/launch_test.go index 8bb65c0..d530af2 100644 --- a/internal/emu/launch_test.go +++ b/internal/emu/launch_test.go @@ -194,7 +194,7 @@ func TestOtherRootBusy(t *testing.T) { if errs.DetailsOf(err)[errs.Reason] != "port_other_root" { t.Errorf("details: %v", errs.DetailsOf(err)) } - if err.Code != errs.Busy || err.Message != `魔改 AVD 实例 3 的端口被另一个根目录的同编号实例占用(pid 4242,D:\ModAVD\avd\mas_3.avd)` || + if err.Code != errs.Busy || err.Message != `ModAVD 实例 3 的端口被另一个根目录的同编号实例占用(pid 4242,D:\ModAVD\avd\mas_3.avd)` || err.Advice == "" { t.Errorf("otherRootBusy = %+v", err) } diff --git a/internal/emu/playstore.go b/internal/emu/playstore.go index 3c23b69..d89f398 100644 --- a/internal/emu/playstore.go +++ b/internal/emu/playstore.go @@ -368,7 +368,7 @@ func (m *Manager) playStoreLock(i int) (*host.FileLock, error) { return nil, err } if !ok { - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正在装卸执行桥、Google Play 商店或改 GLES 3.2 开关,稍后再试", i), + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正在装卸执行桥、Google Play 商店或改 GLES 3.2 开关,稍后再试", i), map[string]string{errs.Reason: "locked", errs.Lock: "bridge"}) } return lock, nil diff --git a/internal/emu/start.go b/internal/emu/start.go index 957f544..b001c0b 100644 --- a/internal/emu/start.go +++ b/internal/emu/start.go @@ -227,7 +227,7 @@ func (m *Manager) start(ctx context.Context, inst *avd.Instance, opt StartOption } } if running { - m.Log.Infof("魔改 AVD 实例 %d 已在运行(pid %d)", i, p.PID) + m.Log.Infof("ModAVD 实例 %d 已在运行(pid %d)", i, p.PID) prog.emit("launch", fmt.Sprintf("实例 %d 已在运行(pid %d),不重新开机", i, p.PID), progressLaunch) adopted, adoptedAt = &p, time.Now() mk, ok := m.readSetupMarker(i) @@ -320,7 +320,7 @@ func (m *Manager) start(ctx context.Context, inst *avd.Instance, opt StartOption if relaunchAfterAdoptedGone(round, opt.watchQemu != nil, err, time.Since(adoptedAt), stopSeen) { // Most likely a guest that was powering off and no longer answered adb, so guestShuttingDown could not // tell: boot the instance as asked. Its setup marker, if any, names a boot that is over now. - m.Log.Infof("魔改 AVD 实例 %d 接管的模拟器(pid %d)没等安卓开机完成就退出了(多半是正在关机),重新开机", + m.Log.Infof("ModAVD 实例 %d 接管的模拟器(pid %d)没等安卓开机完成就退出了(多半是正在关机),重新开机", i, adopted.PID) continue } @@ -376,7 +376,7 @@ func (m *Manager) start(ctx context.Context, inst *avd.Instance, opt StartOption m.removeSetupMarker(i) } if gone { - return nil, errs.Newf(errs.LaunchFailed, "魔改 AVD 实例 %d 在开机后设置期间退出了(模拟器 pid %d):%s", i, + return nil, errs.Newf(errs.LaunchFailed, "ModAVD 实例 %d 在开机后设置期间退出了(模拟器 pid %d):%s", i, qemu.PID, orText(logTail(res.LogFile, 8), "日志见 "+m.Root.LogsDir())) } // Callers write to /sdcard right after start (10-31 measurements): it is not there yet at sys.boot_completed, @@ -495,7 +495,7 @@ func (m *Manager) waitBoot(ctx context.Context, i int, timeout time.Duration, em if !booted.IsZero() { return host.Process{}, m.adbUnusableAfterBoot(i, booted) } - return host.Process{}, errs.Newf(errs.Timeout, "魔改 AVD 实例 %d 启动超时(%.0f 秒内安卓没有启动完成),日志见 %s", + return host.Process{}, errs.Newf(errs.Timeout, "ModAVD 实例 %d 启动超时(%.0f 秒内安卓没有启动完成),日志见 %s", i, timeout.Seconds(), m.Root.LogsDir()) } if !found && emulator != nil && !emulator.Alive() { @@ -503,7 +503,7 @@ func (m *Manager) waitBoot(ctx context.Context, i int, timeout time.Duration, em if c, ok := emulator.ExitCode(); ok { code = strconv.FormatUint(uint64(c), 10) } - return host.Process{}, errs.Newf(errs.LaunchFailed, "魔改 AVD 实例 %d 启动失败(emulator.exe 退出码 %s):%s", + return host.Process{}, errs.Newf(errs.LaunchFailed, "ModAVD 实例 %d 启动失败(emulator.exe 退出码 %s):%s", i, code, logTail(logPath, 8)) } if adopted != nil && stopSeen != nil && !*stopSeen && m.lockHeld(i, "stop") { @@ -512,7 +512,7 @@ func (m *Manager) waitBoot(ctx context.Context, i int, timeout time.Duration, em // both the process snapshot and the PID + creation time check must agree, so a failed snapshot is no exit if !found && adopted != nil && !adopted.Alive() { return host.Process{}, errs.Wrap(errs.LaunchFailed, errAdoptedGone, - fmt.Sprintf("魔改 AVD 实例 %d 的模拟器(pid %d)在等安卓开机时退出了", i, adopted.PID)) + fmt.Sprintf("ModAVD 实例 %d 的模拟器(pid %d)在等安卓开机时退出了", i, adopted.PID)) } if now := time.Now(); !now.Before(nextReport) { elapsed := now.Sub(started) @@ -557,7 +557,7 @@ var registerBooted = func(ctx context.Context, m *Manager, i int) bool { // adbUnusableAfterBoot is the boot wait's error when Android has booted but adb never answered: says so, with the // last line of the adb server's own log. func (m *Manager) adbUnusableAfterBoot(i int, booted time.Time) error { - msg := fmt.Sprintf("魔改 AVD 实例 %d 的安卓已经开好了(模拟器报告开机完成),但之后 %.0f 秒里私有 adb(端口 %d)一直用不了", + msg := fmt.Sprintf("ModAVD 实例 %d 的安卓已经开好了(模拟器报告开机完成),但之后 %.0f 秒里私有 adb(端口 %d)一直用不了", i, time.Since(booted).Seconds(), m.Root.AdbServerPort()) if last := adb.ServerLogLast(m.Root); last != "" { msg += ";adb 自己的日志最后一行:" + last @@ -614,13 +614,13 @@ func poweringOff(v string) bool { // scans for the instance's qemu again: found when someone launched it meanwhile (the start adopts that one). Busy when // p is still there at the end. func (m *Manager) waitShutdown(ctx context.Context, i int, p host.Process, prog *progressor) (host.Process, bool, error) { - m.Log.Infof("魔改 AVD 实例 %d 正在关机(模拟器 pid %d 还没退出),等它关完再开机", i, p.PID) + m.Log.Infof("ModAVD 实例 %d 正在关机(模拟器 pid %d 还没退出),等它关完再开机", i, p.PID) prog.emit("launch", fmt.Sprintf("实例 %d 正在关机,等它关完再开机", i), progressLaunch/2) if !waitQemuExit(ctx, p, shutdownExitWait) { if err := ctxErr(ctx); err != nil { return host.Process{}, false, err } - return host.Process{}, false, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正在关机,等了 %.0f 秒模拟器(pid %d)还没退出", i, + return host.Process{}, false, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正在关机,等了 %.0f 秒模拟器(pid %d)还没退出", i, shutdownExitWait.Seconds(), p.PID).WithAdvice(fmt.Sprintf("稍后再开机;一直关不掉的话先 modavd stop %d", i)), map[string]string{errs.Reason: "stopping"}) } return m.findQemu(i) diff --git a/internal/emu/stop.go b/internal/emu/stop.go index 08b15f7..a57822f 100644 --- a/internal/emu/stop.go +++ b/internal/emu/stop.go @@ -108,7 +108,7 @@ func (m *Manager) Stop(ctx context.Context, i int, opt StopOptions) (*StopResult state, _, _ := mm.creation(i) creating := state == creationCreating && !opt.creating if creating && !opt.CancelCreate { - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正在新建(后台开机做初始化),不能关机", i). + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正在新建(后台开机做初始化),不能关机", i). WithAdvice(fmt.Sprintf("等新建做完;进度用 modavd status 查看;确实要打断请用 modavd stop %d --cancel-create", i)), map[string]string{errs.Reason: "locked", errs.Lock: "create"}) } if creating { @@ -130,7 +130,7 @@ func (m *Manager) Stop(ctx context.Context, i int, opt StopOptions) (*StopResult // already_offline would be a success the start then overrides by booting the instance. Wait a little for its // qemu, still holding the stop lock: a start that has not got past its stop-lock check yet then cannot launch // before this stop is over, and no other stop can get in between (see the header comment). - mm.Log.Infof("魔改 AVD 实例 %d 正在开机,等模拟器起来再关(最多 %.0f 秒)", i, startingQemuWait.Seconds()) + mm.Log.Infof("ModAVD 实例 %d 正在开机,等模拟器起来再关(最多 %.0f 秒)", i, startingQemuWait.Seconds()) var found, starting bool p, found, starting, err = waitStartingQemu(ctx, startingQemuWait, startingQemuPoll, func() (host.Process, bool, error) { return mm.qemuOf(i) }, func() bool { return mm.lockHeld(i, "start") }) @@ -138,7 +138,7 @@ func (m *Manager) Stop(ctx context.Context, i int, opt StopOptions) (*StopResult return nil, err } if !found && starting { - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正在开机,模拟器还没起来,稍后再关", i). + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正在开机,模拟器还没起来,稍后再关", i). WithAdvice("开机前的检查(硬件加速、Vulkan 等)可能要一两分钟"), map[string]string{errs.Reason: "starting"}) } ok = found @@ -147,7 +147,7 @@ func (m *Manager) Stop(ctx context.Context, i int, opt StopOptions) (*StopResult // path, sync included, even if that runs past the budget. launching = found && justLaunched(p, started, mm.booting(i)) if found && !launching { - mm.Log.Warnf("魔改 AVD 实例 %d 等开机时找到的模拟器不是刚拉起的(pid %d),按正常关机处理", i, p.PID) + mm.Log.Warnf("ModAVD 实例 %d 等开机时找到的模拟器不是刚拉起的(pid %d),按正常关机处理", i, p.PID) } } if !ok { @@ -278,12 +278,12 @@ func runStop(ctx context.Context, log *logx.Logger, i int, force bool, started t if err := ops.forceKill(forceKillWait); err != nil { return "force", synced, err } - log.Infof("魔改 AVD 实例 %d 已强制关闭", i) + log.Infof("ModAVD 实例 %d 已强制关闭", i) return "force", synced, nil } if problem != "" { - log.Warnf("魔改 AVD 实例 %d 私有 adb 用不了,不能正常关机也不能 sync,直接用控制台关: %s", i, problem) + log.Warnf("ModAVD 实例 %d 私有 adb 用不了,不能正常关机也不能 sync,直接用控制台关: %s", i, problem) return endQemu(ctx, log, i, "console_kill", false, consoleKillTimeout+closeTimeout, forceKillWait, ops) } // The foreground app goes home first and gets homeWait; the sync below takes what it wrote along (see the header @@ -298,13 +298,13 @@ func runStop(ctx context.Context, log *logx.Logger, i int, force bool, started t } reason := ops.graceful(gracefulWindow(time.Since(started))) if reason == "" { - log.Infof("魔改 AVD 实例 %d 正常关机完成(%.1f s)", i, time.Since(started).Seconds()) + log.Infof("ModAVD 实例 %d 正常关机完成(%.1f s)", i, time.Since(started).Seconds()) return "graceful", true, nil } if err := stopCanceled(ctx, i); err != nil { return "graceful", false, err } - log.Warnf("魔改 AVD 实例 %d 正常关机没成(%s),退回 sync + 控制台 kill", i, reason) + log.Warnf("ModAVD 实例 %d 正常关机没成(%s),退回 sync + 控制台 kill", i, reason) synced := syncBeforeKill(log, i, "", fallbackSyncTimeout, ops.sync) return endQemu(ctx, log, i, "sync_kill", presynced || synced, fallbackKillTimeout(time.Since(started)), fallbackForceKillWait, ops) @@ -315,13 +315,13 @@ func runStop(ctx context.Context, log *logx.Logger, i int, force bool, started t func sendHome(ctx context.Context, log *logx.Logger, i int, home func(time.Duration) (bool, string)) error { sent, failure := home(homeTimeout) if failure != "" { - log.Warnf("魔改 AVD 实例 %d 关机前按 HOME 失败,照常关机: %s", i, failure) + log.Warnf("ModAVD 实例 %d 关机前按 HOME 失败,照常关机: %s", i, failure) return nil } if !sent { return nil } - log.Infof("魔改 AVD 实例 %d 前台的应用已退到后台,等 %.0f 秒让它保存再关机", i, homeWait.Seconds()) + log.Infof("ModAVD 实例 %d 前台的应用已退到后台,等 %.0f 秒让它保存再关机", i, homeWait.Seconds()) select { case <-ctx.Done(): return stopCanceled(ctx, i) @@ -336,9 +336,9 @@ func syncBeforeShutdown(log *logx.Logger, i int, sync func(time.Duration) (bool, started := time.Now() ok, output := sync(shutdownSyncTimeout) if ok { - log.Debugf("魔改 AVD 实例 %d 发关机命令前 sync 完成(%.0f ms)", i, float64(time.Since(started).Microseconds())/1000) + log.Debugf("ModAVD 实例 %d 发关机命令前 sync 完成(%.0f ms)", i, float64(time.Since(started).Microseconds())/1000) } else { - log.Warnf("魔改 AVD 实例 %d 发关机命令前 sync 失败,照常关机: %s", i, output) + log.Warnf("ModAVD 实例 %d 发关机命令前 sync 失败,照常关机: %s", i, output) } return ok } @@ -348,7 +348,7 @@ func syncBeforeShutdown(log *logx.Logger, i int, sync func(time.Duration) (bool, // name, not console_kill: that one means the private adb of a booted guest was unusable and data may be lost; here // there is no guest data yet), never synced. func runLaunchingStop(ctx context.Context, log *logx.Logger, i int, killTimeout time.Duration, ops stopOps) (string, bool, error) { - log.Infof("魔改 AVD 实例 %d 的模拟器刚起来,安卓还没开机,不做正常关机和 sync,直接从控制台关", i) + log.Infof("ModAVD 实例 %d 的模拟器刚起来,安卓还没开机,不做正常关机和 sync,直接从控制台关", i) return endQemu(ctx, log, i, "launching_kill", false, killTimeout, forceKillWait, ops) } @@ -366,13 +366,13 @@ func endQemu(ctx context.Context, log *logx.Logger, i int, method string, synced if err := stopCanceled(ctx, i); err != nil { return method, synced, err } - log.Warnf("魔改 AVD 实例 %d 未在 %.0f 秒内退出,强制结束", i, killTimeout.Seconds()) + log.Warnf("ModAVD 实例 %d 未在 %.0f 秒内退出,强制结束", i, killTimeout.Seconds()) method += "+terminate" if err := ops.forceKill(forceWait); err != nil { return method, synced, err } } - log.Infof("魔改 AVD 实例 %d 已关闭", i) + log.Infof("ModAVD 实例 %d 已关闭", i) return method, synced, nil } @@ -385,9 +385,9 @@ func syncBeforeKill(log *logx.Logger, i int, problem string, timeout time.Durati ok, output = sync(timeout) } if ok { - log.Infof("魔改 AVD 实例 %d 关机前 sync 完成(%.0f ms)", i, float64(time.Since(started).Microseconds())/1000) + log.Infof("ModAVD 实例 %d 关机前 sync 完成(%.0f ms)", i, float64(time.Since(started).Microseconds())/1000) } else { - log.Warnf("魔改 AVD 实例 %d 关机前 sync 失败,照常关机: %s", i, output) + log.Warnf("ModAVD 实例 %d 关机前 sync 失败,照常关机: %s", i, output) } return ok } @@ -508,7 +508,7 @@ func (q qemuKill) run(ctx context.Context, log *logx.Logger, i int, pid uint32, } log.Debugf("结束实例 %d 的 qemu(pid %d)时 Windows 答复: %v(它已经在退出)", i, pid, killErr) } - log.Warnf("魔改 AVD 实例 %d 的 qemu(pid %d)已强制结束,还在退出(等了 %.0f 秒);稍后用 modavd status %d 确认它已退出", + log.Warnf("ModAVD 实例 %d 的 qemu(pid %d)已强制结束,还在退出(等了 %.0f 秒);稍后用 modavd status %d 确认它已退出", i, pid, wait.Seconds(), i) return nil } @@ -524,7 +524,7 @@ func terminateRefused(p host.Process) bool { // qemuNotEnded is the error of a stop after which qemu pid still runs because Windows refused to terminate it // (killErr). func qemuNotEnded(i int, pid uint32, killErr error) error { - return errs.Wrap(errs.Internal, killErr, fmt.Sprintf("魔改 AVD 实例 %d 的 qemu(pid %d)没能结束:%v", i, pid, killErr)). + return errs.Wrap(errs.Internal, killErr, fmt.Sprintf("ModAVD 实例 %d 的 qemu(pid %d)没能结束:%v", i, pid, killErr)). WithAdvice("它可能是以管理员身份启动的(例如 AUTO-MAS 以管理员身份运行时);用管理员权限再关一次,或在任务管理器里结束它") } diff --git a/internal/emu/stop_test.go b/internal/emu/stop_test.go index 1cdfbc5..562633d 100644 --- a/internal/emu/stop_test.go +++ b/internal/emu/stop_test.go @@ -173,7 +173,7 @@ func TestRunStopSendsAppHome(t *testing.T) { f = &stopFake{homeFailure: "返回码 1: error: closed", syncOK: true} method, _, err, warns = runStopFake(t, context.Background(), false, f) if err != nil || method != "graceful" || len(warns) != 1 || - warns[0] != "魔改 AVD 实例 3 关机前按 HOME 失败,照常关机: 返回码 1: error: closed" || + warns[0] != "ModAVD 实例 3 关机前按 HOME 失败,照常关机: 返回码 1: error: closed" || f.at["sync"].Sub(f.at["home"]) >= homeWait { t.Fatalf("failed: %s %v %q", method, err, warns) } @@ -196,7 +196,7 @@ func TestRunStopSyncBeforeShutdownFails(t *testing.T) { if got := strings.Join(f.calls, ","); got != "prepare,home,sync,graceful" { t.Fatalf("calls = %s", got) } - if len(warns) != 1 || warns[0] != "魔改 AVD 实例 3 发关机命令前 sync 失败,照常关机: error: closed" { + if len(warns) != 1 || warns[0] != "ModAVD 实例 3 发关机命令前 sync 失败,照常关机: error: closed" { t.Fatalf("warnings = %q", warns) } } @@ -218,7 +218,7 @@ func TestRunStopFallbackSyncKill(t *testing.T) { if want := stopBudget - gracefulDeadline - fallbackForceKillWait; f.killTimeout > want || f.killTimeout < want-time.Second { t.Fatalf("kill timeout %v, want about %v", f.killTimeout, want) } - if len(warns) != 1 || warns[0] != "魔改 AVD 实例 3 正常关机没成(24 秒内没有自己退出),退回 sync + 控制台 kill" { + if len(warns) != 1 || warns[0] != "ModAVD 实例 3 正常关机没成(24 秒内没有自己退出),退回 sync + 控制台 kill" { t.Fatalf("warnings = %q", warns) } } @@ -260,7 +260,7 @@ func TestRunStopFallbackTerminate(t *testing.T) { if len(warns) != 5 || !strings.Contains(warns[0], "发关机命令前 sync 失败,照常关机: error: closed") || !strings.Contains(warns[2], "关机前 sync 失败,照常关机: error: closed") || warns[3] != "实例 3 控制台 kill 失败,改为强制结束: 连不上控制台 20030" || - warns[4] != "魔改 AVD 实例 3 未在 3 秒内退出,强制结束" { + warns[4] != "ModAVD 实例 3 未在 3 秒内退出,强制结束" { t.Fatalf("warnings = %q", warns) } } @@ -400,7 +400,7 @@ func TestRunStopConsoleOnly(t *testing.T) { if f.killTimeout != consoleKillTimeout+closeTimeout || f.forceKillFor != forceKillWait { t.Fatalf("timeouts kill %v force %v", f.killTimeout, f.forceKillFor) } - if !strings.HasPrefix(warns[0], "魔改 AVD 实例 3 私有 adb 用不了,不能正常关机也不能 sync,直接用控制台关: 私有 adb server") { + if !strings.HasPrefix(warns[0], "ModAVD 实例 3 私有 adb 用不了,不能正常关机也不能 sync,直接用控制台关: 私有 adb server") { t.Fatalf("warnings = %q", warns) } f = &stopFake{problem: "x"} @@ -496,7 +496,7 @@ func TestQemuNotEnded(t *testing.T) { cause := errors.New("Access is denied.") err := qemuNotEnded(3, 4242, cause) if errs.CodeOf(err) != errs.Internal || errs.ExitCode(err) != 1 || !errors.Is(err, cause) || - !strings.HasPrefix(err.Error(), "魔改 AVD 实例 3 的 qemu(pid 4242)没能结束:Access is denied.。") || + !strings.HasPrefix(err.Error(), "ModAVD 实例 3 的 qemu(pid 4242)没能结束:Access is denied.。") || !strings.Contains(err.Error(), "管理员") { t.Fatalf("denied: %v (%s)", err, errs.CodeOf(err)) } @@ -538,7 +538,7 @@ func (k *killScript) run(t *testing.T, ctx context.Context, wait time.Duration) func TestQemuKillRun(t *testing.T) { ctx := context.Background() denied := errors.New("结束qemu-system-x86_64.exe(PID 4242)失败: Access is denied.") - stillExiting := "魔改 AVD 实例 3 的 qemu(pid 4242)已强制结束,还在退出(等了 2 秒);稍后用 modavd status 3 确认它已退出" + stillExiting := "ModAVD 实例 3 的 qemu(pid 4242)已强制结束,还在退出(等了 2 秒);稍后用 modavd status 3 确认它已退出" cases := []struct { name string script killScript diff --git a/internal/emu/window.go b/internal/emu/window.go index a243da4..68fc5d7 100644 --- a/internal/emu/window.go +++ b/internal/emu/window.go @@ -36,10 +36,10 @@ func windowConflict(i int, opt StartOptions, windowed bool) error { const asIs = "(不指定 --window / --headless 就照原样接管)" switch { case opt.Window && !windowed: - return errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正以无头方式运行,运行中不能换成带窗口", i). + return errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正以无头方式运行,运行中不能换成带窗口", i). WithAdvice(fmt.Sprintf("先关机再换:modavd stop %d,再 modavd start %d --window%s", i, i, asIs)), map[string]string{errs.Reason: "window_mismatch"}) case opt.Headless && windowed: - return errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正带窗口运行,运行中不能换成无头", i). + return errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正带窗口运行,运行中不能换成无头", i). WithAdvice(fmt.Sprintf("先关机再换:modavd stop %d,再 modavd start %d --headless%s", i, i, asIs)), map[string]string{errs.Reason: "window_mismatch"}) } return nil @@ -79,15 +79,15 @@ func (m *Manager) SetWindowVisible(ctx context.Context, i int, visible bool) (*W if errs.CodeOf(err) == errs.Unsupported { return nil, err } - m.Log.Warnf("魔改 AVD 实例 %d %s窗口失败: %v", i, verb, err) + m.Log.Warnf("ModAVD 实例 %d %s窗口失败: %v", i, verb, err) return res, nil } res.Windows = n switch { case n > 0: - m.Log.Infof("魔改 AVD 实例 %d 已%s窗口", i, verb) + m.Log.Infof("ModAVD 实例 %d 已%s窗口", i, verb) case visible: - m.Log.Infof("魔改 AVD 实例 %d 是无头开机的,没有窗口可显示;要窗口请关机后带窗口重新启动", i) + m.Log.Infof("ModAVD 实例 %d 是无头开机的,没有窗口可显示;要窗口请关机后带窗口重新启动", i) } return res, nil } diff --git a/internal/jsonord/testdata/gen_pycases.py b/internal/jsonord/testdata/gen_pycases.py index 884ed22..7df2dec 100644 --- a/internal/jsonord/testdata/gen_pycases.py +++ b/internal/jsonord/testdata/gen_pycases.py @@ -15,7 +15,7 @@ "[]", '{"a": {}, "b": [], "c": [{}, []], "d": {"e": {"f": []}}}', '[[], {}, [[]], [{}], {"x": []}]', - '{"名称": "魔改 AVD ✓ 😀", "x": "\\u00e9\\ud83d\\ude00", "": "empty key"}', + '{"名称": "模拟器 ✓ 😀", "x": "\\u00e9\\ud83d\\ude00", "": "empty key"}', '"\\u0000\\u0001\\u001f\\u007f\\b\\f\\n\\r\\t \\u0080 \\u00a0 \\u2028 \\u2029 \\ufffd \\ufeff"', '"a\\"b\\\\c\\/d"', '{"q": "say \\"hi\\"", "p": "C:\\\\sdk-src\\\\sdk-mas35\\\\", "u": "\\u4e2d\\u6587"}', diff --git a/internal/jsonord/testdata/pycases.json b/internal/jsonord/testdata/pycases.json index 4d3f4b9..ff2e08d 100644 --- a/internal/jsonord/testdata/pycases.json +++ b/internal/jsonord/testdata/pycases.json @@ -17,8 +17,8 @@ "out": "[\n [],\n {},\n [\n []\n ],\n [\n {}\n ],\n {\n \"x\": []\n }\n]" }, { -"in": "eyLlkI3np7AiOiAi6a2U5pS5IEFWRCDinJMg8J+YgCIsICJ4IjogIlx1MDBlOVx1ZDgzZFx1ZGUwMCIsICIiOiAiZW1wdHkga2V5In0=", -"out": "{\n \"\u540d\u79f0\": \"\u9b54\u6539 AVD \u2713 \ud83d\ude00\",\n \"x\": \"\u00e9\ud83d\ude00\",\n \"\": \"empty key\"\n}" +"in": "eyLlkI3np7AiOiAi5qih5ouf5ZmoIOKckyDwn5iAIiwgIngiOiAiXHUwMGU5XHVkODNkXHVkZTAwIiwgIiI6ICJlbXB0eSBrZXkifQ==", +"out": "{\n \"\u540d\u79f0\": \"\u6a21\u62df\u5668 \u2713 \ud83d\ude00\",\n \"x\": \"\u00e9\ud83d\ude00\",\n \"\": \"empty key\"\n}" }, { "in": "Ilx1MDAwMFx1MDAwMVx1MDAxZlx1MDA3ZlxiXGZcblxyXHQgXHUwMDgwIFx1MDBhMCBcdTIwMjggXHUyMDI5IFx1ZmZmZCBcdWZlZmYi", diff --git a/internal/mcp/mcp_test.go b/internal/mcp/mcp_test.go index bda2438..d809553 100644 --- a/internal/mcp/mcp_test.go +++ b/internal/mcp/mcp_test.go @@ -102,7 +102,7 @@ func (f *fake) List(ctx context.Context) (*emu.ListResult, error) { func (f *fake) Info(ctx context.Context, i int) (*emu.InstanceInfo, error) { f.record("info %d", i) if i == 9 { - return nil, errs.Newf(errs.NotFound, "魔改 AVD 实例 %d 不存在", i).WithAdvice("先用 list_instances 看有哪些实例") + return nil, errs.Newf(errs.NotFound, "ModAVD 实例 %d 不存在", i).WithAdvice("先用 list_instances 看有哪些实例") } in := sampleInstance(i) in.Diagnostics = f.diagnostics @@ -877,7 +877,7 @@ func TestToolErrors(t *testing.T) { {"get_instance", map[string]any{"index": 3.5}, errs.Usage, "参数 index 必须是整数"}, {"get_instance", map[string]any{"index": 3, "root": `D:\ModAVD`}, errs.Usage, "不认识的参数: root"}, {"list_instances", map[string]any{"all": true}, errs.Usage, "list_instances 不接受参数"}, - {"get_instance", map[string]any{"index": 9}, errs.NotFound, "魔改 AVD 实例 9 不存在。先用 list_instances 看有哪些实例"}, + {"get_instance", map[string]any{"index": 9}, errs.NotFound, "ModAVD 实例 9 不存在。先用 list_instances 看有哪些实例"}, {"delete_instance", map[string]any{"index": 2, "confirm": false}, errs.Usage, "confirm 设为 true"}, {"delete_instance", map[string]any{"index": 2}, errs.Usage, "缺少参数 confirm"}, {"update_instance", map[string]any{"index": 2}, errs.Usage, "至少给一项"}, diff --git a/internal/report/collect.go b/internal/report/collect.go index 7ea7e48..8750be1 100644 --- a/internal/report/collect.go +++ b/internal/report/collect.go @@ -43,7 +43,7 @@ var ( const ( nativeTimeout = 20 * time.Second // Invoke-Native -TimeoutSec default accelTimeout = 30 * time.Second - collectZipStart = "魔改AVD问题信息-" + collectZipStart = "ModAVD-问题信息-" ) // CollectOptions for Collect; the first three are the script's -OutDir, -StageDir and -KeepStage. @@ -241,9 +241,9 @@ func collect(ctx context.Context, rootDir string, h Host, opt CollectOptions) (r }() c := &collector{ctx: ctx, root: rootDir, h: h, opt: opt, stage: stage, start: start, adbPort: root.DefaultAdbServerPort} - c.progress("魔改 AVD 问题信息收集") + c.progress("ModAVD 问题信息收集") c.progress("ModAVD 目录:" + rootDir) - c.L("魔改 AVD 问题信息") + c.L("ModAVD 问题信息") c.L(PrivacyNote) c.L("生成时间:" + start.Format("2006-01-02 15:04:05 -07:00")) // the script names the PowerShell that ran it here @@ -299,7 +299,7 @@ func collect(ctx context.Context, rootDir string, h Host, opt CollectOptions) (r } if !c.masFound { c.L("") - c.L("提示:没找到 AUTO-MAS 安装目录,反馈时请附上 AUTO-MAS 的 debug 文件夹里的日志。") + c.L("提示:" + MasMissingNote) } res := &CollectResult{Stage: stage, Problems: append([]string{}, c.problems...), MasFound: c.masFound, @@ -753,7 +753,7 @@ func (c *collector) runningAndAdb() error { c.L(fmt.Sprintf("pid %d 启动于 %s", q.PID, ct)) cmd := q.CommandLine if cmd == "" { - c.L(" qemu 命令行读不到(多半是 AUTO-MAS 以管理员运行),现场按 adb 设备列表推算") + c.L(" qemu 命令行读不到(多半是模拟器以管理员身份运行),现场按 adb 设备列表推算") c.cmdlineUnreadable = true continue } @@ -1464,7 +1464,7 @@ func (c *collector) autoMAS() error { } } if loc == "" { - c.L("没找到 AUTO-MAS 安装目录,反馈时请附上 AUTO-MAS 的 debug 文件夹里的日志") + c.L("没找到 AUTO-MAS 安装目录(不用 AUTO-MAS 的不用管)") return nil } c.masFound = true @@ -1614,7 +1614,7 @@ func (c *collector) live() error { targets := append([]liveTarget(nil), c.running...) if c.cmdlineUnreadable { // fallback: the emulator- serials of the device list, mapped to instance names with the port plan - c.L("qemu 命令行读不到(多半是 AUTO-MAS 以管理员运行),现场按 adb 设备列表推算") + c.L("qemu 命令行读不到(多半是模拟器以管理员身份运行),现场按 adb 设备列表推算") res := c.adbDevicesCommand() if res.TimedOut { c.L(" adb devices:超时(20 秒),已放弃") diff --git a/internal/report/config.go b/internal/report/config.go index cd64414..db22aa3 100644 --- a/internal/report/config.go +++ b/internal/report/config.go @@ -45,13 +45,13 @@ func GameName(pkg string) (string, bool) { var notGames = []string{"de.jrpie.android.launcher"} // The lines the user fills in. -var configTail = []string{"AUTO-MAS 版本:", "用的脚本(MAA / M9A / …):", "效果(能跑 / 有问题,问题是…):"} +var configTail = []string{"怎么用的(自己玩 / AUTO-MAS 及版本 / MAA、M9A 等脚本):", "效果(能跑 / 有问题,问题是…):"} const ( - configTitle = "【魔改 AVD 试用配置】" - configNotOpen = "游戏包:没有实例开着,读不到。在 AUTO-MAS 里开着实例时再双击运行一次,就会列出来" + configTitle = "【ModAVD 试用配置】" + configNotOpen = "游戏包:没有实例开着,读不到。开着实例时再生成一次,就会列出来" emuCheckWait = 20 * time.Second - clipboardOK = "已复制到剪贴板:粘贴到群里,再把最后三行补上就行。" + clipboardOK = "已复制到剪贴板:粘贴到群里,再把最后两行补上就行。" clipboardError = "复制到剪贴板失败(%s),请手动选中上面的文字复制。" ) diff --git a/internal/report/parity_windows_test.go b/internal/report/parity_windows_test.go index 43eed2e..957e435 100644 --- a/internal/report/parity_windows_test.go +++ b/internal/report/parity_windows_test.go @@ -161,11 +161,26 @@ func modavdPrivacyHead() string { return head } +// reworded: what the scripts said that modavd says differently on purpose, applied in order to the scripts' lines: +// what they said as if every user had AUTO-MAS (many use ModAVD without it), and the old Chinese name for ModAVD +// (it is ModAVD in every language). +var reworded = []struct{ script, modavd string }{ + {"提示:没找到 AUTO-MAS 安装目录,反馈时请附上 AUTO-MAS 的 debug 文件夹里的日志。", "提示:" + MasMissingNote}, + {"没找到 AUTO-MAS 安装目录,反馈时请附上 AUTO-MAS 的 debug 文件夹里的日志", "没找到 AUTO-MAS 安装目录(不用 AUTO-MAS 的不用管)"}, + {"(多半是 AUTO-MAS 以管理员运行)", "(多半是模拟器以管理员身份运行)"}, + {"实例配置、AUTO-MAS 日志(mas 文件夹);", "实例配置;找到 AUTO-MAS 时还有它的日志(mas 文件夹);"}, + {"在 AUTO-MAS 里开着实例时再双击运行一次", "开着实例时再生成一次"}, + {"再把最后三行补上就行。", "再把最后两行补上就行。"}, + {"魔改AVD问题信息-", "ModAVD-问题信息-"}, + {"魔改 AVD", "ModAVD"}, +} + // modavdLine is what modavd writes on purpose instead of a line of collect-info.ps1's summary.txt: // - the PowerShell that ran the script: modavd's version and Go version; // - the adb.exe the live section runs: modavd speaks the adb host protocol itself (no adb.exe, never a server of // its own), to the server that listens on the port; -// - the first line of the privacy note: who does not upload is the manager or the CLI, not a script. +// - the first line of the privacy note: who does not upload is the manager or the CLI, not a script; +// - reworded. func (f *fixture) modavdLine(line string) string { if line == "PowerShell:{{PSVERSION}}" { return "modavd:" + buildinfo.Version + "(" + runtime.Version() + ")" @@ -173,6 +188,9 @@ func (f *fixture) modavdLine(line string) string { if line == scriptPrivacyHead { return modavdPrivacyHead() } + for _, r := range reworded { + line = strings.Replace(line, r.script, r.modavd, 1) + } if strings.HasPrefix(line, "adb:") && strings.Contains(line, " -P "+strconv.Itoa(f.adbPort)+"(") { var pid uint32 for _, l := range f.sc.Listeners { @@ -372,14 +390,17 @@ func TestCollectMatchesScript(t *testing.T) { } } - // the end of the console output: zip path, the privacy note word for word but for its first line + // the end of the console output: zip path, the privacy note word for word but for modavdLine's console, _ := f.golden(t, name, "collect-console.txt") - if !containsLine(lines(console), "文件:"+res.ZipPath) { + var consoleLines []string + for _, l := range lines(console) { + consoleLines = append(consoleLines, f.modavdLine(l)) + } + if !containsLine(consoleLines, "文件:"+res.ZipPath) { t.Errorf("zip %s, the script printed:\n%s", res.ZipPath, console) } - scriptNote := scriptPrivacyHead + strings.TrimPrefix(PrivacyNote, modavdPrivacyHead()) - if !strings.Contains(console, scriptNote) || res.PrivacyNote != PrivacyNote { - t.Error("the privacy note is not the one collect-info.ps1 prints, but for its first line") + if !strings.Contains(strings.Join(consoleLines, "\r\n"), PrivacyNote) || res.PrivacyNote != PrivacyNote { + t.Error("the privacy note is not the one collect-info.ps1 prints, but for modavdLine's") } if strings.Contains(PrivacyNote, "脚本") || !strings.HasPrefix(PrivacyNote, "隐私提示:问题包只保存在本机,") { t.Errorf("privacy note head %q", modavdPrivacyHead()) @@ -441,7 +462,17 @@ func TestReportConfigMatchesScript(t *testing.T) { } // the script prints a blank line, the lines, a blank line and what happened to the clipboard want, _ := f.golden(t, name, "report-config.txt") - wl := lines(want) + var wl []string + for _, l := range lines(want) { + // the script asked for the AUTO-MAS version and the scripts on two lines; modavd asks how it is used + if l == "用的脚本(MAA / M9A / …):" { + continue + } + if l == "AUTO-MAS 版本:" { + l = configTail[0] + } + wl = append(wl, f.modavdLine(l)) + } if len(wl) < 3 || wl[0] != "" || wl[len(wl)-2] != "" { t.Fatalf("unexpected golden shape: %q", wl) } diff --git a/internal/report/report.go b/internal/report/report.go index 7a23d8c..6d20c62 100644 --- a/internal/report/report.go +++ b/internal/report/report.go @@ -30,15 +30,20 @@ import ( "modavd/internal/root" ) -// PrivacyNote is the head of summary.txt and the last thing printed: collect-info.ps1's $PrivacyNote word for word, -// but for the first line. The script's said "脚本不会上传"; from ModAVD 0.38.2 the package has no such script any -// more (ModAVD/ModAVD#45), and what collects and what could upload is the manager or the CLI, so it says those. +// PrivacyNote is the head of summary.txt and the last thing printed: collect-info.ps1's $PrivacyNote, but for the +// first line and the AUTO-MAS logs. The script's said "脚本不会上传"; from ModAVD 0.38.2 the package has no such +// script any more (ModAVD/ModAVD#45), and what collects and what could upload is the manager or the CLI, so it says +// those. The script listed the AUTO-MAS logs as if every user had AUTO-MAS; they are there only when it is found. const PrivacyNote = "隐私提示:问题包只保存在本机,管理器和命令行都不会上传它。\r\n" + - "- 里面有:summary.txt(电脑和模拟器的情况)、模拟器日志、实例配置、AUTO-MAS 日志(mas 文件夹);有实例在运行时还有现场截图和安卓状态(live 文件夹);模拟器近 7 天崩溃过的话还有崩溃转储(crash 文件夹)。不收磁盘镜像,不收 AUTO-MAS 的配置和账号数据,也不会有意收集账号密码。\r\n" + + "- 里面有:summary.txt(电脑和模拟器的情况)、模拟器日志、实例配置;找到 AUTO-MAS 时还有它的日志(mas 文件夹);有实例在运行时还有现场截图和安卓状态(live 文件夹);模拟器近 7 天崩溃过的话还有崩溃转储(crash 文件夹)。不收磁盘镜像,不收 AUTO-MAS 的配置和账号数据,也不会有意收集账号密码。\r\n" + "- 这些文件里都有本机路径,路径里可能有 Windows 用户名;截图和 AUTO-MAS 日志里可能有游戏账号名。\r\n" + "- 可以公开:在 GitHub Issue(https://github.com/ModAVD/ModAVD/issues)里贴 summary.txt 和日志,或者上传删掉 crash 文件夹后的问题包。上传前看一眼,不想公开的内容先删掉。\r\n" + "- 不要公开:crash 文件夹(崩溃转储,含模拟器进程的线程栈和少量内存片段)。带 crash 文件夹的完整问题包请发邮件到 qiyinxi@foxmail.com,邮件里写上 Issue 编号。" +// MasMissingNote: shown after collecting when no AUTO-MAS installation was found. Many users do not use AUTO-MAS, so +// it asks only those who do for its logs. +const MasMissingNote = "没找到 AUTO-MAS 安装目录。用 AUTO-MAS 的,反馈时请另附上它 debug 文件夹里的日志;不用 AUTO-MAS 的不用管。" + // ---- .NET number formatting -------------------------------------------------------------------------------------- // formatN is '{0:N}' -f v of Windows PowerShell 5.1 (.NET Framework, en-US / zh-CN): the double cut to 15 diff --git a/internal/report/report_darwin.go b/internal/report/report_darwin.go index 3856d2d..27e33e8 100644 --- a/internal/report/report_darwin.go +++ b/internal/report/report_darwin.go @@ -536,7 +536,7 @@ func collectDarwin(ctx context.Context, r *root.Root, h Host, opt CollectOptions if len(problems) > 0 { lines = append(lines, "未完成项目:"+strings.Join(problems, ";")) } - summary := "魔改 AVD macOS 问题信息\n" + darwinPrivacyNote + "\n" + sanitize(strings.Join(lines, "\n")) + "\n" + summary := "ModAVD macOS 问题信息\n" + darwinPrivacyNote + "\n" + sanitize(strings.Join(lines, "\n")) + "\n" if err := writeClean("summary.txt", []byte(summary)); err != nil { return nil, err } diff --git a/internal/root/root.go b/internal/root/root.go index 0c4b5f3..c0a4047 100644 --- a/internal/root/root.go +++ b/internal/root/root.go @@ -162,7 +162,7 @@ func (r *Root) checkPorts(allowLegacy bool) *errs.Error { legacy := p.Base == 5500 || p.Base > maxPortBase if problem != "" && !(allowLegacy && legacy) { return errs.New(errs.InvalidArgument, problem).WithAdvice(fmt.Sprintf( - "先关掉这个根目录的所有实例,再把 portBase 改成 %d(和 AUTO-MAS 一样),或者 %d–%d 之间 100 的整数倍,避开 5500(开发和测试用的根目录用 21000);原配置仍可查状态和关机", + "先关掉这个根目录的所有实例,再把 portBase 改成 %d(默认值),或者 %d–%d 之间 100 的整数倍,避开 5500(开发和测试用的根目录用 21000);原配置仍可查状态和关机", DefaultPortBase, minPortBase, maxPortBase)) } if p.Base == DefaultPortBase { @@ -254,10 +254,10 @@ func Resolve(flag string) (*Root, error) { func resolve(flag, env, exe string) (*Root, error) { if s := cleanArg(flag); s != "" { - return openDir(s, "--root 指定的") + return openDir(s, "--root 指定的 ") } if s := cleanArg(env); s != "" { - return openDir(s, "环境变量 "+EnvRoot+" 指定的") + return openDir(s, "环境变量 "+EnvRoot+" 指定的 ") } if exe != "" { dir := filepath.Dir(exe) @@ -267,7 +267,7 @@ func resolve(flag, env, exe string) (*Root, error) { } } } - return nil, errs.New(errs.Usage, "找不到魔改 AVD 根目录").WithAdvice( + return nil, errs.New(errs.Usage, "找不到 ModAVD 根目录").WithAdvice( "用 --root 指定根目录(里面有 sdk 文件夹和 mas-avd.json),或设置环境变量 " + EnvRoot + ";modavd.exe 放在根目录或根目录的 tools 文件夹里时会自动找到") } @@ -287,14 +287,14 @@ func looksLikeRoot(dir string) bool { func openDir(dir, what string) (*Root, error) { abs, err := filepath.Abs(dir) if err != nil { - return nil, errs.Wrap(errs.Usage, err, fmt.Sprintf("%s魔改 AVD 根目录写法不对: %s", what, dir)) + return nil, errs.Wrap(errs.Usage, err, fmt.Sprintf("%sModAVD 根目录写法不对: %s", what, dir)) } fi, err := os.Stat(abs) if err != nil { - return nil, errs.Wrap(errs.NotFound, err, fmt.Sprintf("%s魔改 AVD 根目录不存在: %s", what, abs)) + return nil, errs.Wrap(errs.NotFound, err, fmt.Sprintf("%sModAVD 根目录不存在: %s", what, abs)) } if !fi.IsDir() { - return nil, errs.Newf(errs.NotFound, "%s魔改 AVD 根目录不是文件夹: %s", what, abs) + return nil, errs.Newf(errs.NotFound, "%sModAVD 根目录不是文件夹: %s", what, abs) } return &Root{Dir: abs}, nil } @@ -345,7 +345,7 @@ func (r *Root) QemuImgExe() (string, error) { exe := filepath.Join(r.RuntimeSDK(), "emulator", "qemu-img.exe") if fi, err := os.Stat(exe); err != nil || fi.IsDir() { return exe, errs.Newf(errs.NotReady, "找不到 qemu-img: %s", exe). - WithAdvice("魔改 AVD 的模拟器组件不完整,请更新组件后再试") + WithAdvice("ModAVD 的模拟器组件不完整,请更新组件后再试") } return exe, nil } @@ -677,7 +677,7 @@ func (r *Root) LockInstanceConfig(i int) (*InstanceLock, error) { return nil, errs.Wrap(errs.Internal, err, fmt.Sprintf("实例 %s 的锁文件打不开: %s(%v)", AvdName(i), path, err)) } if !ok { - return nil, errs.WithDetails(errs.Newf(errs.Busy, "魔改 AVD 实例 %d 正被另一个 modavd 改动(新建、改设置或删除),等了 %.0f 秒仍没改完", + return nil, errs.WithDetails(errs.Newf(errs.Busy, "ModAVD 实例 %d 正被另一个 modavd 改动(新建、改设置或删除),等了 %.0f 秒仍没改完", i, configLockWait.Seconds()), map[string]string{errs.Reason: "locked", errs.Lock: "config"}) } return &InstanceLock{l: l}, nil @@ -782,7 +782,7 @@ const ( const ( FetchComponentsAdvice = "在管理器里点「补全组件」,或运行 tools\\modavd.exe components fetch;它会先显示《Android 软件开发套件许可协议》,输入 y 同意后" + "从官方渠道下载 adb 和系统镜像(约 1.5 GB,中断了再运行会接着下),完成后再检查一次" - PackageAdvice = "模拟器要用魔改 AVD 的发布包:从 https://github.com/ModAVD/ModAVD/releases 下载 " + + PackageAdvice = "模拟器要用 ModAVD 的发布包:从 https://github.com/ModAVD/ModAVD/releases 下载 " + "ModAVD-<版本>-win-x64.zip,整个解压到这个目录" ) diff --git a/internal/root/root_test.go b/internal/root/root_test.go index 6322084..bbea507 100644 --- a/internal/root/root_test.go +++ b/internal/root/root_test.go @@ -270,7 +270,7 @@ func TestResolve(t *testing.T) { _, err := resolve(filepath.Join(base, "missing"), "", "") wantCode(t, err, errs.NotFound) - if !strings.Contains(err.Error(), "--root 指定的魔改 AVD 根目录不存在") { + if !strings.Contains(err.Error(), "--root 指定的 ModAVD 根目录不存在") { t.Fatalf("message: %v", err) } _, err = resolve("", filepath.Join(base, "missing"), "") @@ -389,7 +389,7 @@ func TestUpdateMetadataFormatAndAbort(t *testing.T) { writeFile(t, r.MetadataPath(), []byte(sampleMetadata)) err := r.UpdateMetadata(func(o *jsonord.Obj) error { o.Set("adbServerPort", jsonord.NewInt(20089)) - o.Set("名称", jsonord.NewString("魔改 \"AVD\"")) + o.Set("名称", jsonord.NewString("模拟器 \"AVD\"")) return nil }) if err != nil { @@ -397,7 +397,7 @@ func TestUpdateMetadataFormatAndAbort(t *testing.T) { } want := "{\r\n \"components\": {\r\n \"emulator\": \"37.2.10\"\r\n },\r\n \"zeta\": [\r\n 1000.0,\r\n 0,\r\n" + " {}\r\n ],\r\n \"instances\": {\r\n \"0\": {\r\n \"memoryMb\": 6144,\r\n \"initialized\": true\r\n" + - " }\r\n },\r\n \"sdkRoot\": \"\",\r\n \"adbServerPort\": 20089,\r\n \"名称\": \"魔改 \\\"AVD\\\"\"\r\n}" + " }\r\n },\r\n \"sdkRoot\": \"\",\r\n \"adbServerPort\": 20089,\r\n \"名称\": \"模拟器 \\\"AVD\\\"\"\r\n}" if got := string(readFile(t, r.MetadataPath())); got != want { t.Fatalf("got %q\nwant %q", got, want) } diff --git a/internal/ui/fake_test.go b/internal/ui/fake_test.go index fa2a986..e801259 100644 --- a/internal/ui/fake_test.go +++ b/internal/ui/fake_test.go @@ -137,7 +137,7 @@ func (b fakeBackend) Info(ctx context.Context, i int) (*emu.InstanceInfo, error) } in, ok := b.f.instances[i] if !ok { - return nil, errs.Newf(errs.NotFound, "魔改 AVD 实例 %d 不存在", i) + return nil, errs.Newf(errs.NotFound, "ModAVD 实例 %d 不存在", i) } return &in, nil } @@ -174,7 +174,7 @@ func (b fakeBackend) Update(ctx context.Context, i int, req emu.UpdateRequest) ( b.f.mu.Unlock() in, ok := b.f.get(i) if !ok { - return nil, errs.Newf(errs.NotFound, "魔改 AVD 实例 %d 不存在", i) + return nil, errs.Newf(errs.NotFound, "ModAVD 实例 %d 不存在", i) } applied := map[string]any{} if req.FPS != nil { @@ -189,7 +189,7 @@ func (b fakeBackend) Update(ctx context.Context, i int, req emu.UpdateRequest) ( func (b fakeBackend) PreviewDiagnostics(ctx context.Context, i int) (*emu.DiagnosticsPreview, error) { if _, ok := b.f.get(i); !ok { - return nil, errs.Newf(errs.NotFound, "魔改 AVD 实例 %d 不存在", i) + return nil, errs.Newf(errs.NotFound, "ModAVD 实例 %d 不存在", i) } return &emu.DiagnosticsPreview{Index: i, DiagnosticsCleanup: emu.DiagnosticsCleanup{Files: 2, Bytes: 1024}}, nil } @@ -198,7 +198,7 @@ func (b fakeBackend) Delete(ctx context.Context, i int) (*emu.DeleteResult, erro b.f.mu.Lock() defer b.f.mu.Unlock() if _, ok := b.f.instances[i]; !ok { - return nil, errs.Newf(errs.NotFound, "魔改 AVD 实例 %d 不存在", i) + return nil, errs.Newf(errs.NotFound, "ModAVD 实例 %d 不存在", i) } delete(b.f.instances, i) b.f.deleted = append(b.f.deleted, i) @@ -211,7 +211,7 @@ func (b fakeBackend) Start(ctx context.Context, i int, opt emu.StartOptions) (*e gate := b.f.startGate b.f.mu.Unlock() if _, ok := b.f.get(i); !ok { - return nil, errs.Newf(errs.NotFound, "魔改 AVD 实例 %d 不存在", i) + return nil, errs.Newf(errs.NotFound, "ModAVD 实例 %d 不存在", i) } opt.Progress(emu.Progress{Step: "launch", Message: "实例已拉起", Done: 50, Total: 1000}) b.log.Warnf("测试警告 %d", i) @@ -267,7 +267,7 @@ func (b fakeBackend) AppProxy(ctx context.Context, i int) (*emu.AppProxyResult, defer b.f.mu.Unlock() b.f.proxyAsked = append(b.f.proxyAsked, i) if _, ok := b.f.instances[i]; !ok { - return nil, errs.Newf(errs.NotFound, "魔改 AVD 实例 %d 不存在", i) + return nil, errs.Newf(errs.NotFound, "ModAVD 实例 %d 不存在", i) } return b.f.proxyOf(i), nil } diff --git a/internal/ui/proxy_test.go b/internal/ui/proxy_test.go index c6b6c69..05261b5 100644 --- a/internal/ui/proxy_test.go +++ b/internal/ui/proxy_test.go @@ -253,7 +253,7 @@ func TestProxyPackages(t *testing.T) { } // emu says it does not run (no recent list): the same 409 h.f.mu.Lock() - h.f.shellErr = errs.New(errs.Unsupported, "魔改 AVD 实例 3 没有在运行") + h.f.shellErr = errs.New(errs.Unsupported, "ModAVD 实例 3 没有在运行") h.f.mu.Unlock() h.s.hub.dropCached() expectError(t, h.do("GET", "/api/instances/3/packages", nil), 409, errs.Busy) diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 196719f..3236f1d 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -117,7 +117,7 @@ type server struct { func newServer(opt Options) (*server, error) { if opt.Root == nil { - return nil, errs.New(errs.Usage, "没有指定魔改 AVD 根目录") + return nil, errs.New(errs.Usage, "没有指定 ModAVD 根目录") } lg := opt.Log if lg == nil { diff --git a/internal/ui/web/dev/mock.js b/internal/ui/web/dev/mock.js index cc5f9c1..4eb3e63 100644 --- a/internal/ui/web/dev/mock.js +++ b/internal/ui/web/dev/mock.js @@ -232,7 +232,7 @@ function checkResult() { } if (db.check === 'notready' && !db.componentsReady) Object.assign(items[1], { ok: false, reason: '这个目录里缺少:平台工具(adb)、系统镜像 Android 14 Google APIs x86_64', advice: '在管理器里点「补全组件」,或运行 tools\\modavd.exe components fetch;它会先显示《Android 软件开发套件许可协议》,输入 y 同意后从官方渠道下载 adb 和系统镜像(约 1.5 GB,中断了再运行会接着下),完成后再检查一次' }); if (db.check === 'notready') { - Object.assign(items[3], { ok: false, reason: '硬件虚拟化(Windows 虚拟机监控程序平台)不可用,魔改 AVD 无法启动(检查结果:WHPX is not installed)', advice: '先在 BIOS/UEFI 里打开 VT-x/SVM,再在「启用或关闭 Windows 功能」里勾选「Windows 虚拟机监控程序平台」,然后重启' }); + Object.assign(items[3], { ok: false, reason: '硬件虚拟化(Windows 虚拟机监控程序平台)不可用,ModAVD 无法启动(检查结果:WHPX is not installed)', advice: '先在 BIOS/UEFI 里打开 VT-x/SVM,再在「启用或关闭 Windows 功能」里勾选「Windows 虚拟机监控程序平台」,然后重启' }); Object.assign(items[4], { ok: null, reason: '查不了 Vulkan:探测程序 8 秒内没有结束' }); Object.assign(items[2], { reason: '内测包 0.35.0(37.2.10)' }); // the host IME is optional and stays missing, also after a fetch (it is not a downloaded component) @@ -278,7 +278,7 @@ function runImport(j, files) { const steps = []; for (const x of named) if (x.zip || x.slot >= 0) steps.push(['verifying', `校验 ${x.f.name}${x.zip ? '(SHA-1)' : `(${STORE_FILES[x.slot][0]})`}`, x.zip ? STORE_ZIP : STORE_FILES[x.slot][2]]); if (zip) steps.push(['extracting', `解压 ${zip.f.name}`, 3101687808]); - log('info', '魔改 AVD:用自己的文件补全 Google Play 商店', j.id); + log('info', 'ModAVD:用自己的文件补全 Google Play 商店', j.id); let k = 0; const timer = setInterval(() => { if (j.state !== 'running') { clearInterval(timer); return; } @@ -470,7 +470,7 @@ function runDiag(j, kind = j.kind, store = false) { // play_store: Google's Play image too, of which only the five files stay if (store) stages.push(['downloading', 'Google Play 商店(取自 Android 14 Google Play x86_64 镜像 r14)', 420, 1441, 25], ['downloading', 'Google Play 商店(取自 Android 14 Google Play x86_64 镜像 r14)', 1180, 1441, 27], ['extracting', 'Google Play 商店(取自 Android 14 Google Play x86_64 镜像 r14)', 2958, 2958, 0]); - if (kind === 'components') ['魔改 AVD:补全 Google 组件(adb、Android 14 系统镜像)', '已在管理器里同意许可协议。'].forEach((m) => log('info', m, j.id)); + if (kind === 'components') ['ModAVD:补全 Google 组件(adb、Android 14 系统镜像)', '已在管理器里同意许可协议。'].forEach((m) => log('info', m, j.id)); let n = 0; const timer = setInterval(() => { if (j.state !== 'running') { clearInterval(timer); return; } @@ -487,7 +487,7 @@ function runDiag(j, kind = j.kind, store = false) { db.componentsReady = true; if (store) db.store = { ready: true, source: 'download' }; finishJob(j, { root: ROOT, license: 'Android SDK License', license_source: 'Google 中国下载(mock)', components: componentList(), downloads: [] }); - } else finishJob(j, { zip_path: `${ROOT}\\logs\\魔改AVD问题信息-mock.zip`, zip_size: '1.2 MB', problems: [], mas_found: true, entries: ['summary.txt'], + } else finishJob(j, { zip_path: `${ROOT}\\logs\\ModAVD-问题信息-mock.zip`, zip_size: '1.2 MB', problems: [], mas_found: true, entries: ['summary.txt'], privacy_note: '隐私提示(mock):问题包只保存在本机,不会自动上传。\r\n- 可以公开:summary.txt 和日志。\r\n- 不要公开:crash 文件夹。' }); } }, 500); @@ -880,7 +880,7 @@ async function handle(url, init) { if (db.insts.has(i)) return errResp('already_exists', `实例 mas_${i} 已存在`); await sleep(500); const inst = mk(i, body.title, { mem: body.memory_mb, cpu: body.cpu, fps: body.fps, resolution: body.resolution || 1080, disk: body.data_partition_gb, initialized: false, scene: i }); - log('info', `已新建魔改 AVD 实例 ${i}(内存 ${inst.config.memory_mb} MB / ${inst.config.cpu} 核 / 数据盘 ${inst.config.data_partition_gb} GB / 帧率 ${inst.config.fps})`); + log('info', `已新建 ModAVD 实例 ${i}(内存 ${inst.config.memory_mb} MB / ${inst.config.cpu} 核 / 数据盘 ${inst.config.data_partition_gb} GB / 帧率 ${inst.config.fps})`); emit('instances', list()); return json(pub(inst), 201); } @@ -971,7 +971,7 @@ async function handle(url, init) { if (!m) return errResp('not_found', '没有这个接口:' + p); const i = Number(m[1]); const inst = db.insts.get(i); - if (!inst) return errResp('not_found', `魔改 AVD 实例 ${i} 不存在`, '先新建这个实例'); + if (!inst) return errResp('not_found', `ModAVD 实例 ${i} 不存在`, '先新建这个实例'); const act = m[2]; const busyJob = runningJobOf(i); @@ -994,7 +994,7 @@ async function handle(url, init) { } if (method === 'DELETE') { if (url.searchParams.get('confirm') !== '1') return errResp('usage', '删除要带 confirm=1'); - if (inst.running || busyJob) return errResp('busy', `魔改 AVD 实例 ${i} 未关闭,无法删除`, '先关机再删除'); + if (inst.running || busyJob) return errResp('busy', `ModAVD 实例 ${i} 未关闭,无法删除`, '先关机再删除'); await sleep(700); db.insts.delete(i); emit('instances', list()); @@ -1002,9 +1002,9 @@ async function handle(url, init) { } } if (act === 'start') { - if (busyJob) return errResp('busy', `魔改 AVD 实例 ${i} ${busyJob.kind === 'stop' ? '正在关机' : '正在开机或做开机后设置'}`, '等它做完再试'); + if (busyJob) return errResp('busy', `ModAVD 实例 ${i} ${busyJob.kind === 'stop' ? '正在关机' : '正在开机或做开机后设置'}`, '等它做完再试'); // the manager boots with the window only; one running headless is not adopted (no switch while it runs) - if (inst.running && !inst._.win) return errResp('busy', `魔改 AVD 实例 ${i} 正以无头方式运行,运行中不能换成带窗口`, `先关机再换:modavd stop ${i},再 modavd start ${i} --window`); + if (inst.running && !inst._.win) return errResp('busy', `ModAVD 实例 ${i} 正以无头方式运行,运行中不能换成带窗口`, `先关机再换:modavd stop ${i},再 modavd start ${i} --window`); if (inst.status === 'online') { const j = newJob('start', i); run(j, [[400, () => step(j, 'launch', `实例 ${i} 已在运行(pid ${inst.pid}),不重新开机`, 50)], [600, () => step(j, 'boot', `实例 ${i} 安卓已开机`, 610)], @@ -1016,7 +1016,7 @@ async function handle(url, init) { return json(j, 202); } if (act === 'stop') { - if (busyJob && busyJob.kind !== 'start') return errResp('busy', `魔改 AVD 实例 ${i} 正在${busyJob.kind === 'stop' ? '关机' : '安装 APK'}`, '等它做完再试'); + if (busyJob && busyJob.kind !== 'start') return errResp('busy', `ModAVD 实例 ${i} 正在${busyJob.kind === 'stop' ? '关机' : '安装 APK'}`, '等它做完再试'); const j = newJob('stop', i); if (!inst.running) { setTimeout(() => finishJob(j, { index: i, name: inst.name, method: 'already_offline', synced: false, seconds: 0.1, warnings: [] }), 300); @@ -1024,8 +1024,8 @@ async function handle(url, init) { return json(j, 202); } if (act === 'install') { - if (inst.status !== 'online') return errResp('busy', `魔改 AVD 实例 ${i} 没有开机`, '开机后再安装'); - if (busyJob) return errResp('busy', `魔改 AVD 实例 ${i} 正在做别的事`, '等它做完再试'); + if (inst.status !== 'online') return errResp('busy', `ModAVD 实例 ${i} 没有开机`, '开机后再安装'); + if (busyJob) return errResp('busy', `ModAVD 实例 ${i} 正在做别的事`, '等它做完再试'); const f = init.body instanceof FormData ? init.body.get('apk') : null; if (!f) return errResp('usage', '没有收到 APK 文件'); const j = newJob('install', i); @@ -1036,7 +1036,7 @@ async function handle(url, init) { if (act === 'playstore-install' || act === 'playstore-uninstall') { const install = act === 'playstore-install'; const reason = (code, message, advice, r) => fail(STATUS_OF[code], code, message, advice, { reason: r }); - if (busyJob || inst.status === 'creating') return errResp('busy', `魔改 AVD 实例 ${i} 正在做别的事`, '等它做完再试'); + if (busyJob || inst.status === 'creating') return errResp('busy', `ModAVD 实例 ${i} 正在做别的事`, '等它做完再试'); if (sdkJob()) return errResp('busy', `任务 ${sdkJob().kind === 'components' ? '补全组件' : '导入 Google Play 商店的文件'} 正在运行(任务 ${sdkJob().id}),等它做完再试`); if (install && !db.store.ready) { return reason('not_ready', `这个根目录还没有 Google Play 商店的五个文件,或者文件不全、已损坏(${ROOT}\\sdk\\playstore)`, @@ -1051,7 +1051,7 @@ async function handle(url, init) { } if (act === 'gles32-on' || act === 'gles32-off') { const on = act === 'gles32-on'; - if (busyJob || inst.status === 'creating') return errResp('busy', `魔改 AVD 实例 ${i} 正在做别的事`, '等它做完再试'); + if (busyJob || inst.status === 'creating') return errResp('busy', `ModAVD 实例 ${i} 正在做别的事`, '等它做完再试'); if (inst.bridge === 'off') { return fail(STATUS_OF.not_ready, 'not_ready', `实例 ${i} 没有 arm64 执行桥,不按可写系统镜像开机,改不了它报的 GLES 版本`, `先运行 modavd bridge ${i} install 装上执行桥,再开这个开关`, { reason: 'gles32_no_bridge' }); } @@ -1061,7 +1061,7 @@ async function handle(url, init) { } if (act === 'playstore-gsfid') { if (inst.status !== 'online') return errResp('busy', `实例 ${i} 没有开机`, '开机后才能取 GSF ID'); - if (busyJob) return errResp('busy', `魔改 AVD 实例 ${i} 正在做别的事`, '等它做完再试'); + if (busyJob) return errResp('busy', `ModAVD 实例 ${i} 正在做别的事`, '等它做完再试'); if (db.nogsf) { return fail(STATUS_OF.not_ready, 'not_ready', `实例 ${i} 还没有 GSF ID`, 'Google 服务框架联网登记后才有:开着网等一两分钟,再试一次', { reason: 'no_gsf_id' }); } @@ -1084,14 +1084,14 @@ async function handle(url, init) { } else if (act === 'disk-trim') { step(j, 'disk_trim', `实例 ${i} 正在触发磁盘回收`, 0); setTimeout(() => { - if (inst.status !== 'online') return failJob(j, 'unsupported', `魔改 AVD 实例 ${i} 没有在运行,请先开机再回收磁盘空间`, ''); + if (inst.status !== 'online') return failJob(j, 'unsupported', `ModAVD 实例 ${i} 没有在运行,请先开机再回收磁盘空间`, ''); d.alloc = Math.max(3 * GB, d.alloc - 1.5 * GB); finishJob(j, { index: i, discard_max_bytes: 2 * GB }); }, 1800); } else { step(j, 'disk_compact', `实例 ${i} 正在整理磁盘(重写数据盘文件,要几分钟)`, 0); setTimeout(() => { - if (inst.running) return failJob(j, 'busy', `魔改 AVD 实例 ${i} 未关闭,无法整理磁盘`, `先 modavd stop ${i},关机后再整理`); + if (inst.running) return failJob(j, 'busy', `ModAVD 实例 ${i} 未关闭,无法整理磁盘`, `先 modavd stop ${i},关机后再整理`); const before = d.onDisk; d.onDisk = Math.min(before, d.alloc + 0.1 * GB); finishJob(j, { index: i, overlay: `${inst.path}\\userdata-qemu.img.qcow2`, before_on_disk_bytes: Math.round(before), after_on_disk_bytes: Math.round(d.onDisk), freed_bytes: Math.round(before - d.onDisk) }); @@ -1100,7 +1100,7 @@ async function handle(url, init) { return json(j, 202); } if (act === 'input') { - if (inst.status !== 'online') return errResp('busy', `魔改 AVD 实例 ${i} 没有开机`); + if (inst.status !== 'online') return errResp('busy', `ModAVD 实例 ${i} 没有开机`); if (body.type === 'tap') { inst._.tap = { x: body.x, y: body.y, t: now() }; if (inst._.mode !== 'game') inst._.mode = 'game'; @@ -1111,7 +1111,7 @@ async function handle(url, init) { } if (act === 'screenshot') { window.__mockShots[i] = (window.__mockShots[i] || 0) + 1; - if (inst.status !== 'online') return errResp('busy', `魔改 AVD 实例 ${i} 没有开机`); + if (inst.status !== 'online') return errResp('busy', `ModAVD 实例 ${i} 没有开机`); if (db.shotfail) return errResp('internal', '截图失败:adb exec-out screencap 退出码 1', ''); const blob = await screenshot(inst); return new Response(blob, { status: 200, headers: { 'Content-Type': 'image/png', 'Cache-Control': 'no-store' } }); diff --git a/internal/ui/web/js/diagnostics.js b/internal/ui/web/js/diagnostics.js index d7bf663..b333872 100644 --- a/internal/ui/web/js/diagnostics.js +++ b/internal/ui/web/js/diagnostics.js @@ -391,7 +391,7 @@ function collectRows(j) { () => api.post('/api/open', { what: 'collect-info', job: j.id }).catch(toastError)))]; if (r.sections?.length) rows.push(h('ul', { class: 'diag-sections' }, ...r.sections.map((x) => h('li', { class: x.error ? 'tone-err' : 'tone-ok', text: `${x.name}:${x.error || '完成'}` })))); else if (r.problems?.length) rows.push(h('p', { class: 'f-hint', text: `有 ${r.problems.length} 节没收集成功,原因写在包里的 summary.txt,照样提交就行。` })); - if (r.mas_found === false) rows.push(h('p', { class: 'f-hint', text: '没找到 AUTO-MAS 安装目录,反馈时请附上 AUTO-MAS 的 debug 文件夹里的日志。' })); + if (r.mas_found === false) rows.push(h('p', { class: 'f-hint', text: '没找到 AUTO-MAS 安装目录。用 AUTO-MAS 的,反馈时请另附上它 debug 文件夹里的日志;不用 AUTO-MAS 的不用管。' })); return rows; } diff --git a/internal/ui/web/js/update.js b/internal/ui/web/js/update.js index 9404380..9c83936 100644 --- a/internal/ui/web/js/update.js +++ b/internal/ui/web/js/update.js @@ -1,6 +1,7 @@ // 设置 → ModAVD 更新 (API.md "更新"): version and update state of this root, the update source and the Mirror 酱 CDK, -// a check on request. Installing is not offered here yet (AUTO-MAS installs, through `modavd update`, with these -// settings); the button is shown and disabled. +// a check on request. Installing is not offered here yet: AUTO-MAS still updates with its own updater, source and +// CDK, and the two updaters share no lock; the button is shown and disabled. These settings are used by the check +// and by `modavd update`. import { h, icon, btn, attr, setText, showError } from './dom.js'; import { S, update, subscribe } from './store.js'; @@ -87,12 +88,12 @@ export function openUpdate() { h('div', { class: 'f-ctl' }, h('div', { class: 'upd-cdk' }, cdk, save, clear), cdkState), h('label', { class: 'f-label', for: 'upd-comp', text: '组件下载源' }), h('div', { class: 'f-ctl' }, componentSource, componentURLsRow, h('p', { class: 'f-hint', text: '「补全组件」和 modavd components fetch 都从这里选的源下载;选了就保存。' }))); - const note = h('p', { class: 'f-hint', text: '更新现在由 AUTO-MAS 发起(它调用 modavd update),会按这里选的更新源和 CDK 下载;这里先不能直接更新。' }); + const note = h('p', { class: 'f-hint', text: '这里暂时只能检查新版本。用 AUTO-MAS 的,在 AUTO-MAS 里更新(它用 AUTO-MAS 设置里的更新源和 CDK);不用 AUTO-MAS 的,关掉所有实例和管理器,把新版整包覆盖解压到这个文件夹,avd 文件夹不会被动到。这里的更新源和 CDK 只用于「检查更新」和命令行 modavd update。' }); const diagBtn = btn('btn btn-secondary', [icon('shield'), '组件与问题反馈'], () => { ctl.close(); openDiagnostics(); }); const when = h('span', { class: 'foot-note' }); const checkBtn = btn('btn btn-secondary', null, () => run(api.post('/api/update/check'))); const installBtn = btn('btn btn-primary', [icon('download'), '更新'], null, - { disabled: true, 'aria-disabled': 'true', title: '暂时由 AUTO-MAS 发起更新' }); + { disabled: true, 'aria-disabled': 'true', title: '这里暂时不能直接更新' }); for (const r of radios) r.addEventListener('change', () => r.checked && post({ source: r.value }, r.value === 'github' ? '改用 GitHub 更新' : '改用 Mirror 酱更新')); ctl = openDialog({ diff --git a/internal/update/pkg.go b/internal/update/pkg.go index 08075b7..bf35fb2 100644 --- a/internal/update/pkg.go +++ b/internal/update/pkg.go @@ -570,7 +570,7 @@ func checkEmulator(emu string, target Version) error { return packageError("包里模拟器的构建号(%s)和要装的 %s(构建号 %d)不一致", build, target.Display(), target.Build) } if !fileContains(filepath.Join(emu, "qemu", p.QemuDir, p.QEMUHeadless), []byte("free-page-reporting")) { - return packageError("包里的模拟器不是魔改 AVD 的自编版") + return packageError("包里的模拟器不是 ModAVD 的自编版") } return nil } diff --git a/internal/update/swap.go b/internal/update/swap.go index a947cf3..c39c2fe 100644 --- a/internal/update/swap.go +++ b/internal/update/swap.go @@ -361,7 +361,7 @@ func (u *Updater) recoverAfter(err error) error { msg, rerr := u.recoverState() switch { case rerr != nil: - msg = fmt.Sprintf("当场恢复也失败了(%v),下次更新或 AUTO-MAS 启动时会再按记录恢复", rerr) + msg = fmt.Sprintf("当场恢复也失败了(%v),下次更新时会再按记录恢复(用 AUTO-MAS 的,它启动时也会)", rerr) case msg == "": msg = "状态已经完整" } diff --git a/internal/update/updater.go b/internal/update/updater.go index b931076..3e72b5d 100644 --- a/internal/update/updater.go +++ b/internal/update/updater.go @@ -125,7 +125,7 @@ func (u *Updater) notUpdatable() string { return "这个根目录在 mas-avd.json 里设了 sdkRoot(开发用的本地 SDK),不更新" } if !isFile(filepath.Join(u.l.emulator(), root.CurrentPlatform().Emulator)) { - return `这个根目录里没有 sdk\emulator,请先放好魔改 AVD` + return `这个根目录里没有 sdk\emulator,请先放好 ModAVD` } return "" }