Files
Unlucky-Launcher/crates/app/src/host/dispatch.rs
T
AskaEth 5f8382565e
CI / fmt (rustfmt --check) (push) Waiting to run
CI / clippy (ubuntu-latest) (push) Waiting to run
CI / clippy (windows-latest) (push) Waiting to run
CI / test (ubuntu-latest) (push) Waiting to run
CI / test (windows-latest) (push) Waiting to run
CI / build release (ubuntu-latest) (push) Waiting to run
CI / build release (windows-latest) (push) Waiting to run
Guards / core 零 UI 守门 (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
Unlucky Launcher v0.1.0
单提交快照:main 只保留已发布版本的代码树,开发史在 dev。
2026-11-01 20:02:49 +08:00

163 lines
7.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! `CoreHost::dispatch` 的指令实现(从 `host/mod.rs` 拆出,守 400 行上限)。
//!
//! 父模块的私有字段对子模块可见,所以这里直接读写 `self.env` / `self.tx` 等。
use crate::dto::{AppCommand, Backend, CoreEvent, CreateRequest, LoaderKind};
use crate::toast::ToastKind;
use super::accounts::{self, LoginJob};
use super::{game, install, java, launch, logs, settings, versions, CoreHost};
impl CoreHost {
/// 下发一条用户意图(`Backend::dispatch` 的实现体)。
pub(super) fn dispatch_command(&mut self, command: AppCommand) {
self.dispatch_inner(command);
}
}
impl CoreHost {
/// 指令分发(见 `dispatch_command`)。
fn dispatch_inner(&mut self, command: AppCommand) {
match command {
AppCommand::RefreshAll => self.refresh_all(),
AppCommand::LaunchInstance(id) => self.start_launch(&id),
AppCommand::StopInstance(id) => match self.running_pid(&id) {
Some(pid) => match launch::stop(pid) {
Ok(()) => self.toast(ToastKind::Info, "已请求停止游戏进程"),
Err(reason) => self.toast(ToastKind::Error, reason),
},
None => self.toast(ToastKind::Warn, "这个实例当前没有在跑"),
},
AppCommand::ListVersions(_) => versions::spawn_list(self.env.clone(), self.tx.clone()),
AppCommand::InstallVersion(version) => {
let cancel = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
self.install_cancel = Some(std::sync::Arc::clone(&cancel));
install::spawn_install(
self.env.clone(),
self.tx.clone(),
CreateRequest {
name: version.clone(),
version,
loader: LoaderKind::Vanilla,
loader_version: String::new(),
},
cancel,
);
}
AppCommand::CreateInstance(request) => {
let cancel = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
self.install_cancel = Some(std::sync::Arc::clone(&cancel));
install::spawn_install(self.env.clone(), self.tx.clone(), request, cancel);
}
AppCommand::CancelInstall => {
// 取消只在阶段边界生效(core 的下载循环没有取消钩子):这里先把面板收回,
// 后台线程会在下一阶段开始前看到标记并收尾。
if let Some(cancel) = &self.install_cancel {
cancel.store(true, std::sync::atomic::Ordering::SeqCst);
}
self.install = None;
self.send(CoreEvent::InstallProgress(None));
}
AppCommand::SetIsolation { id, mode } => {
match game::set_isolation(&self.env, &id, &mode) {
Ok(()) => {
self.toast(ToastKind::Success, "隔离档位已更新");
self.refresh_instances();
}
Err(reason) => self.toast(ToastKind::Error, reason),
}
}
AppCommand::RefreshLogSessions => {
self.send(CoreEvent::LogSessionsChanged(logs::sessions(&self.env)));
}
AppCommand::OpenLogSession(id) => self.open_session(&id),
AppCommand::ImportCrashLog(path) => match logs::analyze_crash_file(&path) {
Ok((label, report)) => {
self.send(CoreEvent::DiagnosisReady(report));
self.toast(ToastKind::Info, label);
}
Err(reason) => self.toast(ToastKind::Warn, reason),
},
AppCommand::AddOfflineAccount(name) => match accounts::add_offline(&self.env, &name) {
Ok(display) => {
self.toast(ToastKind::Success, format!("已添加离线账户 {display}"));
self.refresh_accounts();
}
Err(reason) => self.toast(ToastKind::Error, reason),
},
AppCommand::RemoveAccount(key) => match accounts::remove(&self.env, &key) {
Ok(display) => {
if self.active_account.as_deref() == Some(key.as_str()) {
self.active_account = None;
}
self.toast(ToastKind::Info, format!("已删除账户 {display}"));
self.refresh_accounts();
}
Err(reason) => self.toast(ToastKind::Error, reason),
},
AppCommand::SwitchAccount(id) => {
self.active_account = Some(id);
self.refresh_accounts();
self.toast(ToastKind::Info, "已切换启动账户");
}
AppCommand::StartDeviceLogin => {
self.login = Some(LoginJob::new());
let job = self.login.clone().unwrap_or_default();
self.send(CoreEvent::DeviceLoginChanged(crate::dto::DeviceLoginView {
phase: crate::dto::DeviceLoginPhase::Requesting,
message: "正在向微软申请设备码…".to_string(),
..crate::dto::DeviceLoginView::new()
}));
accounts::start_login(self.env.clone(), self.tx.clone(), job);
}
AppCommand::StartBrowserLogin => {
self.login = Some(LoginJob::new());
let job = self.login.clone().unwrap_or_default();
self.send(CoreEvent::DeviceLoginChanged(crate::dto::DeviceLoginView {
phase: crate::dto::DeviceLoginPhase::Requesting,
message: "正在准备浏览器登录(本地回调端口)…".to_string(),
..crate::dto::DeviceLoginView::new()
}));
accounts::start_browser_login(self.env.clone(), self.tx.clone(), job);
}
AppCommand::CancelDeviceLogin => {
if let Some(job) = &self.login {
job.cancel();
}
self.login = None;
self.send(CoreEvent::DeviceLoginChanged(
crate::dto::DeviceLoginView::new(),
));
}
AppCommand::InstallJava(vendor) => {
self.java_busy = true;
self.java_message = format!("正在安装 Java({vendor})…");
self.refresh_java();
java::spawn_install(
self.env.clone(),
self.tx.clone(),
unlucky_core::game::java::JdkVendor::parse(&vendor),
);
}
AppCommand::SetSetting { key, value } => match settings::set(&self.env, &key, &value) {
Ok(()) => {
self.send(CoreEvent::SettingsChanged(settings::groups(&self.env)));
self.java_chain.clear();
self.refresh_java();
}
Err(reason) => self.toast(ToastKind::Error, reason),
},
AppCommand::DismissNotice(id) => {
self.notices.retain(|notice| notice.id != id);
self.send(CoreEvent::NoticesChanged(self.notices.clone()));
}
// AI 对话:agent 的 GUI host 未落地前用脚本回复(不接真实模型)
other @ (AppCommand::AskAgent(_) | AppCommand::StopAgent | AppCommand::SetYolo(_)) => {
self.chat.dispatch(other);
for event in self.chat.drain_events() {
self.events.push_back(event);
}
}
}
}
}