mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-06 08:32:51 +08:00
🌱 尝试检测管理员&以管理员模式重启
This commit is contained in:
@@ -36,14 +36,14 @@ walkdir = "2.5.0"
|
||||
[target.'cfg(windows)'.dependencies.windows-sys]
|
||||
version = "0.61.2"
|
||||
features = [
|
||||
"Win32_System_Diagnostics_ToolHelp",
|
||||
"Win32_System_WindowsProgramming",
|
||||
"Win32_System_Pipes",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_Diagnostics",
|
||||
"Win32_System_Diagnostics_Debug",
|
||||
"Win32_System_Diagnostics_ToolHelp",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_Pipes",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_WindowsProgramming",
|
||||
]
|
||||
|
||||
# deep link 插件
|
||||
|
||||
@@ -70,3 +70,81 @@ pub async fn get_dir_size(path: String) -> u64 {
|
||||
}
|
||||
size
|
||||
}
|
||||
|
||||
// 判断是否是管理员权限
|
||||
#[cfg(target_os = "windows")]
|
||||
#[tauri::command]
|
||||
pub fn is_in_admin() -> bool {
|
||||
use windows_sys::Win32::Foundation::HANDLE;
|
||||
use windows_sys::Win32::Security::{
|
||||
AllocateAndInitializeSid, CheckTokenMembership, FreeSid, SID_IDENTIFIER_AUTHORITY, TOKEN_QUERY,
|
||||
};
|
||||
use windows_sys::Win32::System::SystemServices::{
|
||||
DOMAIN_ALIAS_RID_ADMINS, SECURITY_BUILTIN_DOMAIN_RID,
|
||||
};
|
||||
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
|
||||
|
||||
unsafe {
|
||||
let mut token_handle: HANDLE = std::ptr::null_mut();
|
||||
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token_handle) != 0 {
|
||||
let nt_authority = SID_IDENTIFIER_AUTHORITY { Value: [0, 0, 0, 0, 0, 5] };
|
||||
let mut admin_group = std::ptr::null_mut();
|
||||
if AllocateAndInitializeSid(
|
||||
&nt_authority,
|
||||
2,
|
||||
SECURITY_BUILTIN_DOMAIN_RID.try_into().unwrap(),
|
||||
DOMAIN_ALIAS_RID_ADMINS.try_into().unwrap(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&mut admin_group,
|
||||
) != 0
|
||||
{
|
||||
let mut is_admin = 0i32;
|
||||
CheckTokenMembership(std::ptr::null_mut(), admin_group, &mut is_admin);
|
||||
FreeSid(admin_group);
|
||||
return is_admin != 0;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 在 Windows 上以管理员权限重启应用
|
||||
#[cfg(target_os = "windows")]
|
||||
#[tauri::command]
|
||||
pub fn run_with_admin() -> Result<(), String> {
|
||||
use std::ffi::OsStr;
|
||||
use std::iter::once;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::process::exit;
|
||||
use windows_sys::Win32::UI::Shell::ShellExecuteW;
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
|
||||
|
||||
let exe_path = std::env::current_exe().map_err(|e| e.to_string())?;
|
||||
let exe_str: Vec<u16> = OsStr::new(exe_path.to_string_lossy().as_ref())
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let verb: Vec<u16> = OsStr::new("runas").encode_wide().chain(once(0)).collect();
|
||||
|
||||
let result = unsafe {
|
||||
ShellExecuteW(
|
||||
std::ptr::null_mut(),
|
||||
verb.as_ptr(),
|
||||
exe_str.as_ptr(),
|
||||
std::ptr::null(),
|
||||
std::ptr::null(),
|
||||
SW_SHOWNORMAL,
|
||||
)
|
||||
};
|
||||
|
||||
if result as usize > 32 {
|
||||
exit(0);
|
||||
} else {
|
||||
Err("Failed to restart as administrator.".into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ mod utils;
|
||||
mod yae;
|
||||
|
||||
use crate::client::create_mhy_client;
|
||||
use crate::commands::{create_window, execute_js, get_dir_size, init_app};
|
||||
use crate::commands::{
|
||||
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::yae::call_yae_dll;
|
||||
use tauri::{generate_context, generate_handler, Builder, Manager, Window, WindowEvent};
|
||||
@@ -63,7 +65,9 @@ pub fn run() {
|
||||
execute_js,
|
||||
get_dir_size,
|
||||
create_mhy_client,
|
||||
call_yae_dll
|
||||
call_yae_dll,
|
||||
is_in_admin,
|
||||
run_with_admin
|
||||
])
|
||||
.run(generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
@@ -6,17 +6,15 @@ pub mod proto;
|
||||
|
||||
use inject::{call_yaemain, create_named_pipe, find_module_base, inject_dll, spawn_process};
|
||||
use prost::encoding::{decode_key, WireType};
|
||||
use prost::Message;
|
||||
use proto::{parse_achi_list, AchievementInfo};
|
||||
use proto::parse_achi_list;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::windows::io::{FromRawHandle, OwnedHandle, RawHandle};
|
||||
use std::os::windows::io::{FromRawHandle, RawHandle};
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
use windows_sys::Win32::Storage::FileSystem::ReadFile;
|
||||
use windows_sys::Win32::System::Pipes::ConnectNamedPipe;
|
||||
|
||||
// 读取配置值
|
||||
@@ -79,8 +77,6 @@ pub fn parse_achievement_data(bytes: &[u8]) -> Vec<HashMap<u32, u32>> {
|
||||
data
|
||||
}
|
||||
|
||||
use windows_sys::Win32::Foundation::{GetLastError, ERROR_MORE_DATA};
|
||||
|
||||
fn read_u32_le<R: Read>(r: &mut R) -> io::Result<u32> {
|
||||
let mut buf = [0u8; 4];
|
||||
match r.read_exact(&mut buf) {
|
||||
|
||||
@@ -106,15 +106,15 @@ pub struct AchievementInfo {
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
pub struct AchievementEntry {
|
||||
pub struct AchiItemRes {
|
||||
pub id: u32,
|
||||
pub total_progress: u32,
|
||||
pub current_progress: u32,
|
||||
pub finish_timestamp: u32,
|
||||
pub status: u32, // 数值类型
|
||||
pub total: u32,
|
||||
pub cur: u32,
|
||||
pub ts: u32,
|
||||
pub stat: u32,
|
||||
}
|
||||
|
||||
pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<AchievementEntry>, DecodeError> {
|
||||
pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<AchiItemRes>, DecodeError> {
|
||||
let mut cursor = Cursor::new(bytes);
|
||||
let mut dicts: Vec<HashMap<u32, u32>> = Vec::new();
|
||||
|
||||
@@ -146,12 +146,12 @@ pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<AchievementEntry>, DecodeErro
|
||||
|
||||
let achievements = dicts
|
||||
.into_iter()
|
||||
.map(|d| AchievementEntry {
|
||||
.map(|d| AchiItemRes {
|
||||
id: d.get(&15).copied().unwrap_or(0),
|
||||
status: d.get(&11).copied().unwrap_or(0),
|
||||
total_progress: d.get(&8).copied().unwrap_or(0),
|
||||
current_progress: d.get(&13).copied().unwrap_or(0),
|
||||
finish_timestamp: d.get(&7).copied().unwrap_or(0),
|
||||
stat: d.get(&11).copied().unwrap_or(0),
|
||||
total: d.get(&8).copied().unwrap_or(0),
|
||||
cur: d.get(&13).copied().unwrap_or(0),
|
||||
ts: d.get(&7).copied().unwrap_or(0),
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
19
src/enum/yae.ts
Normal file
19
src/enum/yae.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Yae 插件相关枚举类型
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
|
||||
/**
|
||||
* 成就完成状态枚举
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
export const YaeAchiStatEnum: typeof TGApp.Plugins.Yae.AchiItemStat = {
|
||||
/** 无效状态 */
|
||||
Invalid: 0,
|
||||
/** 未完成 */
|
||||
Unfinished: 1,
|
||||
/** 已完成未领取奖励 */
|
||||
Finished: 2,
|
||||
/** 已领取奖励 */
|
||||
RewardTaken: 3,
|
||||
};
|
||||
@@ -76,11 +76,13 @@ import showSnackbar from "@comp/func/snackbar.js";
|
||||
import TuaAchiList from "@comp/userAchi/tua-achi-list.vue";
|
||||
import TuaSeries from "@comp/userAchi/tua-series.vue";
|
||||
import TSUserAchi from "@Sqlm/userAchi.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import { path } from "@tauri-apps/api";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { open, save } from "@tauri-apps/plugin-dialog";
|
||||
import { writeTextFile } from "@tauri-apps/plugin-fs";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { exists, writeTextFile } from "@tauri-apps/plugin-fs";
|
||||
import { platform } from "@tauri-apps/plugin-os";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import {
|
||||
getUiafHeader,
|
||||
@@ -88,15 +90,20 @@ import {
|
||||
verifyUiafData,
|
||||
verifyUiafDataClipboard,
|
||||
} from "@utils/UIAF.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, onUnmounted, ref, shallowRef, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
import { AppAchievementSeriesData } from "@/data/index.js";
|
||||
|
||||
const seriesList = AppAchievementSeriesData.sort((a, b) => a.order - b.order).map((s) => s.id);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { gameDir } = storeToRefs(useAppStore());
|
||||
|
||||
let achiListener: UnlistenFn | null = null;
|
||||
let yaeListener: UnlistenFn | null = null;
|
||||
|
||||
const search = ref<string>("");
|
||||
const isSearch = ref<boolean>(false);
|
||||
@@ -124,6 +131,21 @@ onMounted(async () => {
|
||||
await handleImportOuter(route.query.app);
|
||||
}
|
||||
achiListener = await listen<void>("updateAchi", async () => await refreshOverview());
|
||||
yaeListener = await listen<TGApp.Plugins.Yae.AchiListRes>(
|
||||
"yae_achi_list",
|
||||
(e: Event<TGApp.Plugins.Yae.AchiListRes>) => tryParseYaeAchi(e.payload),
|
||||
);
|
||||
});
|
||||
|
||||
onUnmounted(async () => {
|
||||
if (achiListener !== null) {
|
||||
achiListener();
|
||||
achiListener = null;
|
||||
}
|
||||
if (yaeListener !== null) {
|
||||
yaeListener();
|
||||
yaeListener = null;
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => uidCur.value, refreshOverview);
|
||||
@@ -278,15 +300,36 @@ async function deleteUid(): Promise<void> {
|
||||
}
|
||||
|
||||
async function toYae(): Promise<void> {
|
||||
await openUrl("https://github.com/HolographicHat/Yae");
|
||||
if (platform() !== "windows") {
|
||||
showSnackbar.warn("MacOS暂不支持该功能");
|
||||
return;
|
||||
}
|
||||
if (gameDir.value === "未设置") {
|
||||
showSnackbar.warn("请前往设置页面设置游戏安装目录");
|
||||
return;
|
||||
}
|
||||
const gamePath = `${gameDir.value}${path.sep()}YuanShen.exe`;
|
||||
if (!(await exists(gamePath))) {
|
||||
showSnackbar.warn("未检测到原神本体应用!");
|
||||
return;
|
||||
}
|
||||
// 判断是否是管理员权限
|
||||
const isAdmin = await invoke<boolean>("is_in_admin");
|
||||
if (!isAdmin) {
|
||||
const check = await showDialog.check("是否以管理员模式重启?", "该功能需要管理员权限才能使用");
|
||||
if (!check) {
|
||||
showSnackbar.cancel("已取消以管理员模式重启");
|
||||
return;
|
||||
}
|
||||
await invoke("run_with_admin");
|
||||
}
|
||||
await invoke("call_yae_dll", { gamePath: gamePath });
|
||||
}
|
||||
|
||||
onUnmounted(async () => {
|
||||
if (achiListener !== null) {
|
||||
achiListener();
|
||||
achiListener = null;
|
||||
}
|
||||
});
|
||||
async function tryParseYaeAchi(payload: TGApp.Plugins.Yae.AchiListRes): Promise<void> {
|
||||
console.log(payload);
|
||||
showSnackbar.success(`已收到来自Yae的成就数据,共${payload.list.length}条`);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.achi-prepend {
|
||||
|
||||
51
src/types/Plugins/Yae.d.ts
vendored
Normal file
51
src/types/Plugins/Yae.d.ts
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Yae 插件类型定义
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
|
||||
declare namespace TGApp.Plugins.Yae {
|
||||
/**
|
||||
* 后端返的成就列表数据
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
type AchiListRes = Array<AchiItemRes>;
|
||||
|
||||
/**
|
||||
* 成就完成状态
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
const AchiItemStat = <const>{
|
||||
/* 无效状态 */
|
||||
Invalid: 0,
|
||||
/* 未完成 */
|
||||
Unfinished: 1,
|
||||
/* 已完成未领取奖励 */
|
||||
Finished: 2,
|
||||
/* 已领取奖励 */
|
||||
RewardTaken: 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* 成就完成状态m枚举
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
type AchiItemStatEnum = (typeof AchiItemStat)[keyof typeof AchiItemStat];
|
||||
|
||||
/**
|
||||
* 后端返的单个成就数据
|
||||
* @since Beta v0.7.8
|
||||
* @see src-tauri/yae/proto.rs AchiItemRes
|
||||
*/
|
||||
type AchiItemRes = {
|
||||
/* 成就 ID */
|
||||
id: number;
|
||||
/* 成就总进度 */
|
||||
total: number;
|
||||
/* 成就当前进度 */
|
||||
cur: number;
|
||||
/* 完成时间戳,单位秒 */
|
||||
ts: number;
|
||||
/* 成就完成状态 */
|
||||
stat: AchiItemStatEnum;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user