mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-23 22:09:51 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc03edd30b | ||
|
|
d18b6bb898 | ||
|
|
0f107abde6 | ||
|
|
46cf40734f |
@@ -7,7 +7,12 @@ Update: 2025-12-03
|
|||||||
|
|
||||||
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2025-09-09 14:30:56`
|
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2025-09-09 14:30:56`
|
||||||
>
|
>
|
||||||
> 更新于 `2025-12-03 10:22:56`
|
> 更新于 `2025-12-03 20:02:17`
|
||||||
|
|
||||||
|
## [0.8.8](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.8) (2025-12-03)
|
||||||
|
|
||||||
|
- 🐛 修复成就数据读取异常
|
||||||
|
- 🐛 重构管理员权限重启逻辑
|
||||||
|
|
||||||
## [0.8.7](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.7) (2025-12-03)
|
## [0.8.7](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.7) (2025-12-03)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "teyvatguide",
|
"name": "teyvatguide",
|
||||||
"version": "0.8.7",
|
"version": "0.8.8",
|
||||||
"description": "Game Tool for GenshinImpact player",
|
"description": "Game Tool for GenshinImpact player",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.24.0",
|
"packageManager": "pnpm@10.24.0",
|
||||||
|
|||||||
28
src-tauri/Cargo.lock
generated
28
src-tauri/Cargo.lock
generated
@@ -4,7 +4,7 @@ version = 4
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "TeyvatGuide"
|
name = "TeyvatGuide"
|
||||||
version = "0.8.7"
|
version = "0.8.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"log",
|
"log",
|
||||||
@@ -2398,9 +2398,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.28"
|
version = "0.4.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"value-bag",
|
"value-bag",
|
||||||
]
|
]
|
||||||
@@ -5011,7 +5011,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-deep-link"
|
name = "tauri-plugin-deep-link"
|
||||||
version = "2.4.5"
|
version = "2.4.5"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
"dunce",
|
||||||
"plist",
|
"plist",
|
||||||
@@ -5031,7 +5031,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-dialog"
|
name = "tauri-plugin-dialog"
|
||||||
version = "2.4.2"
|
version = "2.4.2"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
@@ -5048,7 +5048,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-fs"
|
name = "tauri-plugin-fs"
|
||||||
version = "2.4.4"
|
version = "2.4.4"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"dunce",
|
"dunce",
|
||||||
@@ -5069,7 +5069,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-http"
|
name = "tauri-plugin-http"
|
||||||
version = "2.5.4"
|
version = "2.5.4"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"cookie_store",
|
"cookie_store",
|
||||||
@@ -5092,7 +5092,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-log"
|
name = "tauri-plugin-log"
|
||||||
version = "2.7.1"
|
version = "2.7.1"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_logger",
|
"android_logger",
|
||||||
"byte-unit",
|
"byte-unit",
|
||||||
@@ -5113,7 +5113,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-opener"
|
name = "tauri-plugin-opener"
|
||||||
version = "2.5.2"
|
version = "2.5.2"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
"dunce",
|
||||||
"glob",
|
"glob",
|
||||||
@@ -5134,7 +5134,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-os"
|
name = "tauri-plugin-os"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gethostname",
|
"gethostname",
|
||||||
"log",
|
"log",
|
||||||
@@ -5151,7 +5151,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-process"
|
name = "tauri-plugin-process"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
@@ -5160,7 +5160,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-shell"
|
name = "tauri-plugin-shell"
|
||||||
version = "2.3.3"
|
version = "2.3.3"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"log",
|
"log",
|
||||||
@@ -5180,7 +5180,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-single-instance"
|
name = "tauri-plugin-single-instance"
|
||||||
version = "2.3.6"
|
version = "2.3.6"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -5194,7 +5194,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-sql"
|
name = "tauri-plugin-sql"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#1483c63101aef6ee7ac4701ec91f0a2e5c912ad4"
|
source = "git+ssh://git@github.com/tauri-apps/plugins-workspace.git?branch=v2#d9d51eb8ea2fd6ce3298ec0dc370cc04351a9b98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"indexmap 2.12.1",
|
"indexmap 2.12.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "TeyvatGuide"
|
name = "TeyvatGuide"
|
||||||
version = "0.8.7"
|
version = "0.8.8"
|
||||||
description = "Game Tool for Genshin Impact player"
|
description = "Game Tool for Genshin Impact player"
|
||||||
authors = ["BTMuli <bt-muli@outlook.com>"]
|
authors = ["BTMuli <bt-muli@outlook.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -21,7 +21,7 @@ tauri-build = { version = "2.5.3", features = [] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.42"
|
chrono = "0.4.42"
|
||||||
log = "0.4.28"
|
log = "0.4.29"
|
||||||
prost = "0.14.1"
|
prost = "0.14.1"
|
||||||
prost-types = "0.14.1"
|
prost-types = "0.14.1"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
message AchievementProtoFieldInfo {
|
|
||||||
uint32 id = 1;
|
|
||||||
uint32 status = 2;
|
|
||||||
uint32 total_progress = 3;
|
|
||||||
uint32 current_progress = 4;
|
|
||||||
uint32 finish_timestamp = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AchievementItem {
|
|
||||||
uint32 pre = 1;
|
|
||||||
uint32 group = 2;
|
|
||||||
string name = 3;
|
|
||||||
string description = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message MethodRvaConfig {
|
|
||||||
uint32 do_cmd = 1;
|
|
||||||
uint32 update_normal_prop = 3;
|
|
||||||
uint32 new_string = 4;
|
|
||||||
uint32 find_game_object = 5;
|
|
||||||
uint32 event_system_update = 6;
|
|
||||||
uint32 simulate_pointer_click = 7;
|
|
||||||
uint32 to_int32 = 8;
|
|
||||||
uint32 tcp_state_ptr = 9;
|
|
||||||
uint32 shared_info_ptr = 10;
|
|
||||||
uint32 decompress = 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
message NativeLibConfig {
|
|
||||||
uint32 store_cmd_id = 1;
|
|
||||||
uint32 achievement_cmd_id = 2;
|
|
||||||
map<uint32, MethodRvaConfig> method_rva = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AchievementInfo {
|
|
||||||
string version = 1;
|
|
||||||
map<uint32, string> group = 2;
|
|
||||||
map<uint32, AchievementItem> items = 3;
|
|
||||||
AchievementProtoFieldInfo pb_info = 4;
|
|
||||||
NativeLibConfig native_config = 5;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//! 命令模块,负责处理命令
|
//! 命令模块,负责处理命令
|
||||||
//! @since Beta v0.7.8
|
//! @since Beta v0.8.8
|
||||||
|
|
||||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
|
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
|
||||||
use tauri_utils::config::{WebviewUrl, WindowConfig};
|
use tauri_utils::config::{WebviewUrl, WindowConfig};
|
||||||
@@ -127,58 +127,3 @@ pub fn is_in_admin() -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 以管理员权限重启应用
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn run_with_admin() -> Result<(), String> {
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
{
|
|
||||||
return Err("This function is only supported on Windows.".into());
|
|
||||||
}
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
{
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::iter::once;
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
|
||||||
use std::ptr::null_mut;
|
|
||||||
use windows_sys::Win32::Foundation::HWND;
|
|
||||||
use windows_sys::Win32::UI::Shell::ShellExecuteW;
|
|
||||||
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
|
|
||||||
|
|
||||||
fn to_wide(s: &OsStr) -> Vec<u16> {
|
|
||||||
s.encode_wide().chain(once(0)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
let exe_path = std::env::current_exe().map_err(|e| e.to_string())?;
|
|
||||||
if !exe_path.exists() {
|
|
||||||
return Err(format!("executable not found: {}", exe_path.display()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let elevated_arg = "--elevated-action=post_install";
|
|
||||||
// /C start "" "<full_path>" --elevated-action=post_install
|
|
||||||
let params = format!("/C start \"\" \"{}\" {}", exe_path.display(), elevated_arg);
|
|
||||||
|
|
||||||
let cmd_w = to_wide(OsStr::new("cmd.exe"));
|
|
||||||
let verb_w = to_wide(OsStr::new("runas"));
|
|
||||||
let params_w = to_wide(OsStr::new(¶ms));
|
|
||||||
let workdir_w =
|
|
||||||
exe_path.parent().map(|p| to_wide(p.as_os_str())).unwrap_or_else(|| to_wide(OsStr::new("")));
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let result = ShellExecuteW(
|
|
||||||
0 as HWND,
|
|
||||||
verb_w.as_ptr(),
|
|
||||||
cmd_w.as_ptr(),
|
|
||||||
params_w.as_ptr(),
|
|
||||||
if workdir_w.len() > 1 { workdir_w.as_ptr() } else { null_mut() },
|
|
||||||
SW_SHOWNORMAL,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result as usize) > 32 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err("Failed to restart as administrator via cmd.".into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
//! 主模块,用于启动应用
|
//! 主模块,用于启动应用
|
||||||
//! @since Beta v0.7.8
|
//! @since Beta v0.8.8
|
||||||
|
|
||||||
mod client;
|
mod client;
|
||||||
mod commands;
|
mod commands;
|
||||||
mod plugins;
|
mod plugins;
|
||||||
mod utils;
|
mod utils;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
mod watchdog;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
mod yae;
|
mod yae;
|
||||||
|
|
||||||
use crate::client::create_mhy_client;
|
use crate::client::create_mhy_client;
|
||||||
use crate::commands::{
|
use crate::commands::{create_window, execute_js, get_dir_size, init_app, is_in_admin};
|
||||||
create_window, execute_js, get_dir_size, init_app, is_in_admin, run_with_admin,
|
|
||||||
};
|
|
||||||
use crate::plugins::{build_log_plugin, build_si_plugin};
|
use crate::plugins::{build_log_plugin, build_si_plugin};
|
||||||
#[cfg(target_os = "windows")]
|
use tauri::{generate_context, generate_handler, Manager, Window, WindowEvent};
|
||||||
use crate::yae::call_yae_dll;
|
|
||||||
use tauri::{generate_context, generate_handler, Builder, Manager, Window, WindowEvent};
|
|
||||||
|
|
||||||
// 窗口事件处理
|
// 窗口事件处理
|
||||||
fn window_event_handler(app: &Window, event: &WindowEvent) {
|
fn window_event_handler(app: &Window, event: &WindowEvent) {
|
||||||
@@ -40,9 +38,35 @@ fn window_event_handler(app: &Window, event: &WindowEvent) {
|
|||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
Builder::default()
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
let is_watchdog = args.iter().any(|a| a == "--watchdog");
|
||||||
|
// 看门狗模式:不初始化 Tauri,不加载单例,纯等待 + 提权启动
|
||||||
|
if is_watchdog {
|
||||||
|
// 解析父进程 PID
|
||||||
|
let mut ppid: u32 = 0;
|
||||||
|
for a in &args {
|
||||||
|
if let Some(rest) = a.strip_prefix("--ppid=") {
|
||||||
|
if let Ok(v) = rest.parse::<u32>() {
|
||||||
|
ppid = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 等父进程退出后再 runas 启动管理员实例,传入 --elevated 标志
|
||||||
|
let _ = watchdog::run_watchdog(ppid, "--elevated");
|
||||||
|
// 看门狗退出
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常应用实例:加载单例插件,防止多实例
|
||||||
|
let mut builder = tauri::Builder::default();
|
||||||
|
|
||||||
|
// 只有在正常/管理员实例下才加载单例插件;看门狗不加载
|
||||||
|
builder = builder.plugin(build_si_plugin());
|
||||||
|
builder
|
||||||
.on_window_event(move |app, event| window_event_handler(app, event))
|
.on_window_event(move |app, event| window_event_handler(app, event))
|
||||||
.plugin(build_si_plugin())
|
|
||||||
.plugin(tauri_plugin_deep_link::init())
|
.plugin(tauri_plugin_deep_link::init())
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
.plugin(tauri_plugin_fs::init())
|
.plugin(tauri_plugin_fs::init())
|
||||||
@@ -67,10 +91,11 @@ pub fn run() {
|
|||||||
execute_js,
|
execute_js,
|
||||||
get_dir_size,
|
get_dir_size,
|
||||||
create_mhy_client,
|
create_mhy_client,
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
call_yae_dll,
|
|
||||||
is_in_admin,
|
is_in_admin,
|
||||||
run_with_admin
|
#[cfg(target_os = "windows")]
|
||||||
|
yae::call_yae_dll,
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
watchdog::run_with_admin
|
||||||
])
|
])
|
||||||
.run(generate_context!())
|
.run(generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|||||||
@@ -12,19 +12,13 @@ use tauri_plugin_single_instance::init;
|
|||||||
pub fn build_si_plugin<R: Runtime>() -> TauriPlugin<R> {
|
pub fn build_si_plugin<R: Runtime>() -> TauriPlugin<R> {
|
||||||
init(move |app, argv, _cwd| {
|
init(move |app, argv, _cwd| {
|
||||||
// 把 argv 转成 Vec<String>
|
// 把 argv 转成 Vec<String>
|
||||||
let args: Vec<String> = argv.iter().map(|s| s.to_string()).collect();
|
// let args: Vec<String> = argv.iter().map(|s| s.to_string()).collect();
|
||||||
|
|
||||||
// 如果包含提升约定参数,发出专门事件并短路退出
|
// 如果包含提升约定参数,发出专门事件并短路退出
|
||||||
if args.iter().any(|a| a.starts_with("--elevated-action"))
|
// if args.iter().any(|a| a == "--elevated") {
|
||||||
|| args.iter().any(|a| a == "--elevated")
|
// 提升实例通常只负责传参或执行一次性任务,退出避免与主实例冲突
|
||||||
{
|
// std::process::exit(0);
|
||||||
if let Err(e) = app.emit("elevated_launch", args.clone()) {
|
// }
|
||||||
// 记录错误但不要 panic
|
|
||||||
eprintln!("emit elevated_launch failed: {}", e);
|
|
||||||
}
|
|
||||||
// 提升实例通常只负责传参或执行一次性任务,退出避免与主实例冲突
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 非提升启动:按原逻辑广播 deep link
|
// 非提升启动:按原逻辑广播 deep link
|
||||||
if let Err(e) = app.emit("active_deep_link", argv) {
|
if let Err(e) = app.emit("active_deep_link", argv) {
|
||||||
|
|||||||
79
src-tauri/src/watchdog.rs
Normal file
79
src-tauri/src/watchdog.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
//! 重启提权相关处理
|
||||||
|
//! @since Beta v0.8.7
|
||||||
|
#![cfg(target_os = "windows")]
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::iter::once;
|
||||||
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tauri::AppHandle;
|
||||||
|
use windows_sys::Win32::Foundation::{HANDLE, HWND};
|
||||||
|
use windows_sys::Win32::Storage::FileSystem::SYNCHRONIZE;
|
||||||
|
use windows_sys::Win32::System::Threading::{OpenProcess, WaitForSingleObject, INFINITE};
|
||||||
|
use windows_sys::Win32::UI::Shell::ShellExecuteW;
|
||||||
|
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
|
||||||
|
|
||||||
|
// 带参数启动
|
||||||
|
fn shell_runas_with_args(args: &str) -> Result<(), String> {
|
||||||
|
fn to_wide(s: &OsStr) -> Vec<u16> {
|
||||||
|
s.encode_wide().chain(once(0)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
let exe_path = std::env::current_exe().map_err(|e| e.to_string())?;
|
||||||
|
let exe_wide = to_wide(exe_path.as_os_str());
|
||||||
|
let args_wide = to_wide(OsStr::new(args));
|
||||||
|
let cwd_wide =
|
||||||
|
exe_path.parent().map(|p| to_wide(p.as_os_str())).unwrap_or_else(|| to_wide(OsStr::new("")));
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let result = ShellExecuteW(
|
||||||
|
0 as HWND,
|
||||||
|
to_wide(OsStr::new("runas")).as_ptr(),
|
||||||
|
exe_wide.as_ptr(),
|
||||||
|
args_wide.as_ptr(),
|
||||||
|
if cwd_wide.len() > 1 { cwd_wide.as_ptr() } else { null_mut() },
|
||||||
|
SW_SHOWNORMAL,
|
||||||
|
);
|
||||||
|
if (result as usize) > 32 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("Failed to ShellExecuteW runas".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待父进程退出(释放单例锁)后,再以管理员身份启动新实例
|
||||||
|
pub fn run_watchdog(parent_pid: u32, args_to_pass: &str) -> Result<(), String> {
|
||||||
|
// 打开父进程句柄用于等待
|
||||||
|
let handle: HANDLE = unsafe { OpenProcess(SYNCHRONIZE, 0, parent_pid) };
|
||||||
|
if handle == std::ptr::null_mut() {
|
||||||
|
// 如果拿不到句柄,可能父进程已退出,稍作等待后继续
|
||||||
|
std::thread::sleep(Duration::from_millis(300));
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
WaitForSingleObject(handle, INFINITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 父进程已退出 → 触发 UAC 提权启动新实例
|
||||||
|
shell_runas_with_args(args_to_pass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以管理员权限重启应用
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn run_with_admin(app_handle: AppHandle) -> Result<(), String> {
|
||||||
|
let parent_pid = std::process::id();
|
||||||
|
let exe = std::env::current_exe().map_err(|e| e.to_string())?;
|
||||||
|
let mut cmd = std::process::Command::new(exe);
|
||||||
|
cmd
|
||||||
|
.arg("--watchdog")
|
||||||
|
.arg(format!("--ppid={}", parent_pid))
|
||||||
|
// 看门狗不加载单例插件(通过参数决定 main 的初始化)
|
||||||
|
.spawn()
|
||||||
|
.map_err(|e| format!("spawn watchdog failed: {e}"))?;
|
||||||
|
|
||||||
|
// 立即退出:单例锁释放
|
||||||
|
app_handle.exit(0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ fn read_rva(key: &str) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 读取配置文件
|
// 读取配置文件
|
||||||
fn read_conf(path: &str) -> i32 {
|
pub fn read_conf(path: &str) -> i32 {
|
||||||
// 编译时嵌入 JSON 文件,值都是32位整数
|
// 编译时嵌入 JSON 文件,值都是32位整数
|
||||||
let data = include_str!("../../lib/conf.json");
|
let data = include_str!("../../lib/conf.json");
|
||||||
let json: Value = serde_json::from_str(data).expect("Invalid JSON");
|
let json: Value = serde_json::from_str(data).expect("Invalid JSON");
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
//! Yae 成就信息的 Protobuf 定义
|
//! Yae 成就信息的 Protobuf 定义
|
||||||
//! @since Beta v0.8.7
|
//! @since Beta v0.7.9
|
||||||
#![cfg(target_os = "windows")]
|
#![cfg(target_os = "windows")]
|
||||||
|
|
||||||
|
use crate::yae::read_conf;
|
||||||
use prost::encoding::{decode_key, WireType};
|
use prost::encoding::{decode_key, WireType};
|
||||||
use prost::DecodeError;
|
use prost::DecodeError;
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
@@ -140,6 +141,8 @@ pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<UiafAchiItem>, DecodeError> {
|
|||||||
let value = prost::encoding::decode_varint(&mut inner)? as u32;
|
let value = prost::encoding::decode_varint(&mut inner)? as u32;
|
||||||
dict.insert(tag, value);
|
dict.insert(tag, value);
|
||||||
}
|
}
|
||||||
|
// 输出 dict
|
||||||
|
println!("{:?}", dict);
|
||||||
// dict 至少需要两个 key
|
// dict 至少需要两个 key
|
||||||
if dict.len() > 2 {
|
if dict.len() > 2 {
|
||||||
dicts.push(dict)
|
dicts.push(dict)
|
||||||
@@ -147,14 +150,20 @@ pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<UiafAchiItem>, DecodeError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _id = read_conf("id") as u32;
|
||||||
|
let _status = read_conf("status") as u32;
|
||||||
|
let _cur = read_conf("currentProgress") as u32;
|
||||||
|
let _ts = read_conf("finishTimestamp") as u32;
|
||||||
|
|
||||||
let achievements = dicts
|
let achievements = dicts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|d| UiafAchiItem {
|
.map(|d| UiafAchiItem {
|
||||||
id: d.get(&15).copied().unwrap_or(0),
|
id: d.get(&_id).copied().unwrap_or(0),
|
||||||
status: d.get(&11).copied().unwrap_or(0),
|
status: d.get(&_status).copied().unwrap_or(0),
|
||||||
current: d.get(&13).copied().unwrap_or(0),
|
current: d.get(&_cur).copied().unwrap_or(0),
|
||||||
timestamp: d.get(&7).copied().unwrap_or(0),
|
timestamp: d.get(&_ts).copied().unwrap_or(0),
|
||||||
})
|
})
|
||||||
|
.filter(|a| a.status != 0)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(achievements)
|
Ok(achievements)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "TeyvatGuide",
|
"productName": "TeyvatGuide",
|
||||||
"identifier": "TeyvatGuide",
|
"identifier": "TeyvatGuide",
|
||||||
"version": "0.8.7",
|
"version": "0.8.8",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm vite:dev",
|
"beforeDevCommand": "pnpm vite:dev",
|
||||||
"beforeBuildCommand": "pnpm vite:build",
|
"beforeBuildCommand": "pnpm vite:build",
|
||||||
|
|||||||
@@ -350,6 +350,7 @@ async function toYae(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function tryParseYaeAchi(payload: TGApp.Plugins.Yae.AchiListRes): Promise<void> {
|
async function tryParseYaeAchi(payload: TGApp.Plugins.Yae.AchiListRes): Promise<void> {
|
||||||
|
console.log(payload);
|
||||||
if (payload.length === 0) {
|
if (payload.length === 0) {
|
||||||
showSnackbar.warn(`未从Yae获取到成就数据`);
|
showSnackbar.warn(`未从Yae获取到成就数据`);
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user