Skip to content

feat: 续传下载器、一键诊断/求助包、离线启动、崩溃对话框、测速选源;审查问题修复 - #26

Merged
gaoyu06 merged 24 commits into
masterfrom
fix/launcher-review
Oct 16, 2026
Merged

gaoyu06 merged 24 commits into
masterfrom
fix/launcher-review

Conversation

@gaoyu06

@gaoyu06 gaoyu06 commented Oct 16, 2026

Copy link
Copy Markdown
Member

Rust 侧修复 launcher-core(launcher-core-3 被反驳,未改),前端修复 launcher-ui-1~24,并完成 product-4、product-7。
新功能:统一续传下载器(可取消、重试)、运行时/实例体检与修复、导出求助包(已脱敏)、离线/快速启动、游戏崩溃对话框(用户同意后上传)、下载源测速自动选择。开机自启默认改为关闭,首次运行时询问。
验证:cargo test 117 通过、cargo build、npm typecheck/test/build 通过。没有在真机上点过一遍,需要手测:崩溃对话框、诊断修复、中途取消、离线启动。

- New downloader module: .part temp files appended to the full file name,
  Range resume guarded by If-Range/checksum, per-segment retry and resume for
  parallel downloads, exponential backoff with mirror failover, 30s idle read
  timeout instead of 30 minutes, per-session and global cancellation, and
  throttled progress callbacks.
- Migrate every download path to it: Mojang/Zulu runtimes, launcher mod and
  Edge AOT packages, Extreme packages, manifest files, modpacks, content
  installs, launcher updates and vanilla libraries/assets.
- Managed Java runtimes are installed into a staging directory and promoted
  only after a completion marker with per-file sizes/SHA1 is written; an
  incomplete runtime is repaired instead of reused.
- Launcher mod packages, Edge AOT packages and manifest files now require a
  sha256 before install.
- Old launcher installers in updates/ are cleaned up; metadata clients use a
  60s timeout.
- New command cancel_all_downloads; ensure_jdk, install_native_app,
  install_modpack and download_launcher_app_update accept an optional
  ipcSession for cancellation.
Each start_launcher_heartbeat creates its own handle; stop wakes the sleeping
loop through a condvar, and the loop reads the cached session on every beat
instead of a snapshot (launcher-core-5).
- rename/duplicate/export instance, list_instance_section_entries and
  extract_background_theme_accent now run on the blocking pool
  (launcher-core-6); background images use the short API client.
- rename/duplicate/export validate version ids with ensure_safe_child_name
  before joining them under versions/ (launcher-core-20).
- Instance and world archive imports stream from disk instead of reading the
  whole zip into memory (launcher-core-13).
Unreadable jars in mods/ are logged and skipped during the runtime
compatibility scan; a damaged launcher-managed jar marks the package for
reinstall instead of failing the state query (launcher-core-9).
Accepted sockets are switched back to blocking with a 5s read timeout, and
requests that are not a GET on the redirect path or that carry a foreign
state are answered and ignored instead of ending the login. The callback
page escapes its message (launcher-core-10).
…lures

- The master key is resolved once per process instead of on every get/set.
- Keychain failures carry stable codes CREDENTIAL_STORE_UNAVAILABLE /
  CREDENTIAL_STORE_ACCESS_DENIED as the error prefix.
- New commands secure_storage_status and secure_storage_set_file_fallback:
  with the user's consent the master key can live in a 0600 file when the
  system store is unusable; a file key is never created while credentials
  encrypted with an unreachable keychain key exist (launcher-core-11).
Both run through run_install_subprocess: output drained on background
threads, polled with try_wait, killed on install-session cancel or after a
15 minute ceiling (launcher-core-12).
- Extreme updates keep fpsmaster_worlds.json (launcher-core-4).
- A swap whose old copy cannot be deleted is reported as success with a
  warning; leftover backups are cleaned on the next swap (launcher-core-18).
- Extreme asset extraction goes through a staging dir with a completion
  marker, so an interrupted extraction is redone (launcher-core-15).
- terminate_game_process only kills pids the launcher started, and refuses
  when the pid now belongs to a newer process (launcher-core-16).
Edge/Nova now receive the FPSMaster token in the FPSMASTER_LAUNCH_TOKEN
environment variable. The -Dfpsmaster.auth.token argument stays for one
release for already-shipped clients (TODO in push_platform_token). Launch
results returned to the UI no longer contain the platform or Minecraft
access token (product-7, launcher-core-17).
… probing

- game-exit now carries versionId, durationMs, crashed, terminatedByUser and
  the crash report / hs_err path and summary written during the run.
- upload_game_crash_report sends a redacted crash report to
  /api/v1/telemetry/client-errors; HTTP 429 resolves as rate_limited.
- run_health_check returns status, i18n code, params and a repair command
  for Java runtimes, instance files, disk space, download sources, the
  credential store and recent crashes; repair_java_runtime re-verifies a
  managed runtime and fetches only bad files.
- export_support_bundle writes a redacted zip with launcher log, latest game
  log, crash reports, hs_err files, health check and system info.
- probe_download_sources measures official vs BMCLAPI (and the FPSMaster
  API); the new "auto" download source id resolves to the faster one.
- Secrets (JWTs, launch token, access tokens, Authorization, cookies,
  passwords) are stripped before anything is uploaded or exported.
push_ui_log runs lines that look like they carry a token, password, cookie or
Authorization header through the shared redaction rules (CONTRACTS.md 6).
- get_offline_launch_readiness checks an installed instance purely locally
  (profile, loader/OptiFine, libraries, client jar, assets, managed Java) so
  the UI can skip catalog lookups and update checks and launch directly.
- list_forge_versions / list_optifine_versions remember their last good
  result on disk and fall back to it, with a warning, when the network
  lookup fails (launcher-ui-3, backend side).
from_id("auto") ran the source probes synchronously whenever the cached
measurement was missing or older than 30 minutes, so an offline or
already-installed launch waited on network probes. The probe now refreshes
in the background and the last known answer (or mirror-first) is used.
…ithout going offline-broken

Runtimes installed before completion markers existed are adopted when the
tree looks like a finished JRE (java, class library, JVM library, no
download temp files, no staging sibling); a marker is generated from the
current files. Interrupted installs still go through repair.

The installed runtime is now moved aside only after an installer has
fetched its remote metadata, so an offline launch or health-check repair
leaves a working runtime in place. Repair no longer deletes the marker up
front, and it refuses to swap a Zulu runtime for jre-legacy or an emulated
build when Azul is unreachable. A Mojang install over a tree left by a
Zulu archive prunes files the manifest does not list.
The Rust API client now asks the backend for the UI language and turns every
failed FPSMaster API response into a tagged error carrying status, code and
the localized message. The frontend branches on that code: only a 401 with a
session code logs the user out, mirror 401/403s no longer do, transport
failures name the download host instead of blaming the FPSMaster server, and
cancelled, integrity and disk-full failures get their own text.

Adds a Node test-runner suite for the classifier (npm test).
…d cancel

Home, instances, servers and quick join now share launchInstance, so the
single-instance check, the release gate and the Extreme branch apply
everywhere; Extreme can join a server through launch_native_app.

An installed instance no longer needs the network to start: Forge/OptiFine
lookups, content auto-updates and preset package updates are best-effort,
a missing catalog is retried once and otherwise the installed build starts,
and a network failure offers "launch without updating" when the files are
complete. A quick-launch button skips update checks entirely.

The prepare dialog can be cancelled (cancel_install plus checkpoints between
steps), errors are shown once instead of in two stacked modals, and
check-instance no longer hides Forge/OptiFine progress. Nova package status
is written under its per-version key too, the dead post-login package sync is
gone, and the log poll keeps a fixed 500ms rhythm. Dialogs get role=dialog,
focus trapping and Escape.
… content task store

Content installs, uninstalls, world imports and modpacks now run as global
tasks: leaving the Content page keeps their progress, the same project cannot
be started twice, launching or repairing an instance that is being written to
is refused, and every task can be cancelled (Modrinth/CurseForge installs take
an ipcSession). Search, trending and installed-list requests only apply the
latest response, so switching instances cannot show another instance's mods.

The Extreme package download reports byte progress and speed instead of a
fixed 20%. Repair runs in the install dialog with progress and cancel, honors
the download source and thread settings, force-reinstalls Extreme's native
package and refreshes every preset badge afterwards. The launcher self-update
download can be cancelled, and closing the window during downloads asks
whether to minimize to the tray or cancel and quit.
A new Diagnostics page runs run_health_check for the selected instance and
shows every item with a localized title and explanation keyed by its code.
Items carry their repair: per-item Fix, Fix all for repairs that need no
input, Repair instance for an incomplete Extreme client or unreadable
profile, and a link to the download source settings. Running fixes can be
cancelled. The credential file-key fallback is only enabled after explicit
consent, also offered on the credential-store error screen.

Export support bundle asks where to save (export_support_bundle gained an
optional targetPath). Crash uploads now tag per-version Nova ids as NOVA.
The launcher now listens to game-exit. Any exit clears the running-game
state; a crash (not a user stop) brings the main window back and shows the
crash summary with buttons to open the crash report, the JVM error log and
the logs folder, to jump to diagnostics for that instance, and to send the
redacted report after the user agrees (HTTP 429 is dropped silently).
Settings offer "Auto" (the faster of Mojang and BMCLAPI, falling back to the
other on failure), now the default for new installs, and a speed test that
shows latency and throughput per source with a one-click switch to the
recommended one.
…n gaps

- Start-with-computer is off by default and asked once; the first close to
  tray explains where the window went.
- Background images are copied into the app data folder and only the path is
  stored, settings writes are debounced and a failing localStorage write no
  longer crashes the UI.
- Reset to defaults asks first and keeps the game directory and CurseForge
  key.
- Cancelling a Microsoft login closes the OAuth window and ignores a late
  result instead of adding the account anyway.
- Uninstalled presets show "not installed" instead of "update available".
- Remaining hard-coded Chinese/English strings move to the locale files and
  the mod actions are reachable by keyboard.
…, launch guard

Install validation messages and duplicate/export results were written only to
the hidden status line; they are toasts now. The launch guard is reserved
before the first await and cleared directly, so a double click cannot start
two launches and a launch that ends before React renders cannot leave the
guard stuck. Drops the unused hard-coded fallback news item.
Login, news, dashboard, home and app update parsers ran a strict JSON parse
before the status check, so a proxy 502 HTML page or a plain-text framework
404 surfaced as raw English parse errors instead of a status-tagged API
error the frontend can word in zh/en.
…y check

The macOS traffic-light close, Alt+F4, Cmd+Q and tray Quit bypassed
closeLauncherWindow, so they quit mid-download without confirmation or
cancel_all_downloads, and hid to the tray without the first-time notice.
The main window frontend now handles CloseRequested and a quit-requested
event and claims the decision from the backend; Cmd+Q is a custom app menu
item because the predefined one terminates immediately. Without a live
frontend the backend keeps its old behaviour.
@gaoyu06
gaoyu06 merged commit 4a2c0ec into master Oct 16, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant