mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-06 08:32:51 +08:00
✨ 完成成就导入
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
//! @file src/commands.rs
|
||||
//! @desc 命令模块,负责处理命令
|
||||
//! @since Beta v0.7.4
|
||||
//! 命令模块,负责处理命令
|
||||
//! @since Beta v0.7.8
|
||||
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
|
||||
use tauri_utils::config::{WebviewUrl, WindowConfig};
|
||||
@@ -72,9 +71,13 @@ pub async fn get_dir_size(path: String) -> u64 {
|
||||
}
|
||||
|
||||
// 判断是否是管理员权限
|
||||
#[cfg(target_os = "windows")]
|
||||
#[tauri::command]
|
||||
pub fn is_in_admin() -> bool {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
Err("This function is only supported on Windows.".into())
|
||||
}
|
||||
|
||||
use windows_sys::Win32::Foundation::HANDLE;
|
||||
use windows_sys::Win32::Security::{
|
||||
AllocateAndInitializeSid, CheckTokenMembership, FreeSid, SID_IDENTIFIER_AUTHORITY, TOKEN_QUERY,
|
||||
@@ -113,10 +116,14 @@ pub fn is_in_admin() -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// 在 Windows 上以管理员权限重启应用
|
||||
#[cfg(target_os = "windows")]
|
||||
// 以管理员权限重启应用
|
||||
#[tauri::command]
|
||||
pub fn run_with_admin() -> Result<(), String> {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
Err("This function is only supported on Windows.".into())
|
||||
}
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::iter::once;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
||||
@@ -104,6 +104,10 @@ fn read_exact_vec<R: Read>(r: &mut R, len: usize) -> io::Result<Vec<u8>> {
|
||||
/// 调用 dll
|
||||
#[tauri::command]
|
||||
pub fn call_yae_dll(app_handle: AppHandle, game_path: String) -> () {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
Err("This function is only supported on Windows.".into())
|
||||
}
|
||||
let dll_path = app_handle.path().resource_dir().unwrap().join("resources/YaeAchievementLib.dll");
|
||||
dbg!(&dll_path);
|
||||
// 0. 创建 YaeAchievementPipe 的 命名管道,获取句柄
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
//! Yae 成就信息的 Protobuf 定义
|
||||
//! @since Beta v0.9.0
|
||||
//! @since Beta v0.8.7
|
||||
|
||||
use prost::encoding::{decode_key, WireType};
|
||||
use prost::DecodeError;
|
||||
use prost::Message;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::io::Read;
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
#[derive(Clone, PartialEq, Message, Serialize)]
|
||||
pub struct AchievementProtoFieldInfo {
|
||||
@@ -106,15 +105,14 @@ pub struct AchievementInfo {
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
pub struct AchiItemRes {
|
||||
pub struct UiafAchiItem {
|
||||
pub id: u32,
|
||||
pub total: u32,
|
||||
pub cur: u32,
|
||||
pub ts: u32,
|
||||
pub stat: u32,
|
||||
pub current: u32,
|
||||
pub timestamp: u32,
|
||||
pub status: u32,
|
||||
}
|
||||
|
||||
pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<AchiItemRes>, DecodeError> {
|
||||
pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<UiafAchiItem>, DecodeError> {
|
||||
let mut cursor = Cursor::new(bytes);
|
||||
let mut dicts: Vec<HashMap<u32, u32>> = Vec::new();
|
||||
|
||||
@@ -146,13 +144,13 @@ pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<AchiItemRes>, DecodeError> {
|
||||
|
||||
let achievements = dicts
|
||||
.into_iter()
|
||||
.map(|d| AchiItemRes {
|
||||
.map(|d| UiafAchiItem {
|
||||
id: d.get(&15).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),
|
||||
status: d.get(&11).copied().unwrap_or(0),
|
||||
current: d.get(&13).copied().unwrap_or(0),
|
||||
timestamp: d.get(&7).copied().unwrap_or(0),
|
||||
})
|
||||
.filter(|a| a.timestamp != 0)
|
||||
.collect();
|
||||
|
||||
Ok(achievements)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* 成就完成状态枚举
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
export const YaeAchiStatEnum: typeof TGApp.Plugins.Yae.AchiItemStat = {
|
||||
export const UiafAchiStatEnum: typeof TGApp.Plugins.UIAF.AchiItemStat = {
|
||||
/** 无效状态 */
|
||||
Invalid: 0,
|
||||
/** 未完成 */
|
||||
@@ -79,7 +79,7 @@ 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 { listen, type UnlistenFn, type Event } from "@tauri-apps/api/event";
|
||||
import { open, save } from "@tauri-apps/plugin-dialog";
|
||||
import { exists, writeTextFile } from "@tauri-apps/plugin-fs";
|
||||
import { platform } from "@tauri-apps/plugin-os";
|
||||
@@ -133,7 +133,14 @@ onMounted(async () => {
|
||||
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),
|
||||
async (e: Event<string>) => {
|
||||
try {
|
||||
await tryParseYaeAchi(JSON.parse(e.payload));
|
||||
} catch (err) {
|
||||
await TGLogger.Error(`[Achievements][yae_achi_list] 解析Yae成就数据失败: ${err}`);
|
||||
showSnackbar.error(`解析Yae成就数据失败:${err}`);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -327,8 +334,28 @@ async function toYae(): Promise<void> {
|
||||
}
|
||||
|
||||
async function tryParseYaeAchi(payload: TGApp.Plugins.Yae.AchiListRes): Promise<void> {
|
||||
console.log(payload);
|
||||
showSnackbar.success(`已收到来自Yae的成就数据,共${payload.list.length}条`);
|
||||
console.log(typeof payload, payload);
|
||||
if (payload.length === 0) {
|
||||
showSnackbar.warn(`未从Yae获取到成就数据`);
|
||||
return;
|
||||
}
|
||||
const input = await showDialog.input("请输入存档UID", "UID:", uidCur.value.toString());
|
||||
if (!input) {
|
||||
showSnackbar.cancel("已取消存档导入");
|
||||
return;
|
||||
}
|
||||
if (input === "" || isNaN(Number(input))) {
|
||||
showSnackbar.warn("请输入合法数字");
|
||||
return;
|
||||
}
|
||||
await showLoading.start("正在导入成就数据", `UID:${input},数量:${payload.length}`);
|
||||
await TSUserAchi.mergeUiaf(payload, input);
|
||||
await showLoading.end();
|
||||
showSnackbar.success("导入成功,即将刷新页面");
|
||||
await TGLogger.Info("[Achievements][handleImportOuter] 导入成功");
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 1500));
|
||||
await router.push("/achievements");
|
||||
window.location.reload();
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* @file plugins/Sqlite/modules/userAchi.ts
|
||||
* @description 用户成就模块
|
||||
* @since Beta v0.6.0
|
||||
* 用户成就模块
|
||||
* @since Beta v0.8.7
|
||||
*/
|
||||
|
||||
import { UiafAchiStatEnum } from "@enum/uiaf.js";
|
||||
import { path } from "@tauri-apps/api";
|
||||
import { exists, mkdir, readDir, readTextFile, writeTextFile } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
@@ -155,11 +155,16 @@ async function getAchievements(
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 查找成就数据
|
||||
* @since Beta v0.6.0
|
||||
* 查找成就数据
|
||||
* @since Beta v0.8.7
|
||||
* @remarks
|
||||
* 支持三种搜索方式:
|
||||
* 1. 版本搜索:输入 vx.x 格式的关键词(如 v1.2),搜索对应版本的成就
|
||||
* 2. ID搜索:输入 ixxx 格式的关键词(如 i1001),搜索对应ID的成就
|
||||
* 3. 名称/描述搜索:输入任意关键词,搜索成就名称或描述中包含该关键词的成就
|
||||
* @param {number} uid - 存档 UID
|
||||
* @param {string} keyword - 关键词
|
||||
* @returns {Promise<TGApp.Sqlite.Achievement.RenderAchi[]>} 成就数据
|
||||
* @returns {Promise<Array<TGApp.Sqlite.Achievement.RenderAchi>>} 成就数据
|
||||
*/
|
||||
async function searchAchi(
|
||||
uid: number,
|
||||
@@ -167,11 +172,15 @@ async function searchAchi(
|
||||
): Promise<TGApp.Sqlite.Achievement.RenderAchi[]> {
|
||||
if (keyword === "") return await getAchievements(uid);
|
||||
const versionReg = /^v\d+(\.\d+)?$/;
|
||||
let rawData: TGApp.App.Achievement.Item[];
|
||||
const res: TGApp.Sqlite.Achievement.RenderAchi[] = [];
|
||||
const idReg = /^i\d+$/;
|
||||
let rawData: Array<TGApp.App.Achievement.Item>;
|
||||
const res: Array<TGApp.Sqlite.Achievement.RenderAchi> = [];
|
||||
if (versionReg.test(keyword)) {
|
||||
const version = keyword.replace("v", "");
|
||||
rawData = AppAchievementsData.filter((i) => i.version.includes(version));
|
||||
} else if (idReg.test(keyword)) {
|
||||
const id = parseInt(keyword.replace("i", ""));
|
||||
rawData = AppAchievementsData.filter((a) => a.id === id);
|
||||
} else {
|
||||
rawData = AppAchievementsData.filter((a) => {
|
||||
if (a.name.includes(keyword)) return true;
|
||||
@@ -312,16 +321,19 @@ async function restoreUiaf(dir: string): Promise<boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 导入Uiaf数据
|
||||
* @since Beta v0.6.0
|
||||
* @param {TGApp.Plugins.UIAF.Achievement[]} data - 成就数据
|
||||
* 导入Uiaf数据
|
||||
* @since Beta v0.7.8
|
||||
* @param {Array<TGApp.Plugins.UIAF.Achievement>} data - 成就数据
|
||||
* @param {number} uid - 存档UID
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function mergeUiaf(data: TGApp.Plugins.UIAF.Achievement[], uid: number): Promise<void> {
|
||||
async function mergeUiaf(data: Array<TGApp.Plugins.UIAF.Achievement>, uid: number): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
for (const achi of data) {
|
||||
const status = achi.status === 2 || achi.status === 3 ? 1 : 0;
|
||||
const status =
|
||||
achi.status === UiafAchiStatEnum.Finished || achi.status === UiafAchiStatEnum.RewardTaken
|
||||
? 1
|
||||
: 0;
|
||||
const timeC = status === 1 ? timestampToDate(achi.timestamp * 1000) : "";
|
||||
const timeN = timestampToDate(new Date().getTime());
|
||||
await db.execute(
|
||||
|
||||
83
src/types/Plugins/UIAF.d.ts
vendored
83
src/types/Plugins/UIAF.d.ts
vendored
@@ -1,60 +1,69 @@
|
||||
/**
|
||||
* @file types/Plugins/UIAF.d.ts
|
||||
* @description UIAF 插件类型定义文件
|
||||
* @since Beta v0.6.0
|
||||
* UIAF 插件类型定义文件
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description UIAF 插件类型命名空间
|
||||
* @namespace TGApp.Plugins.UIAF
|
||||
* @merberof TGApp.Plugins
|
||||
* @since Beta v0.6.0
|
||||
*/
|
||||
declare namespace TGApp.Plugins.UIAF {
|
||||
/**
|
||||
* @interface Data
|
||||
* UIAF完整数据
|
||||
* @since Alpha v0.1.5
|
||||
* @description UIAF 成就数据
|
||||
* @property {Export} info UIAF 头部信息
|
||||
* @property {Achievement[]} list UIAF 成就列表
|
||||
* @return Data
|
||||
*/
|
||||
interface Data {
|
||||
type Data = {
|
||||
/* UIAF 头部信息 */
|
||||
info: Export;
|
||||
list: Achievement[];
|
||||
}
|
||||
/* UIAF 成就列表 */
|
||||
list: Array<Achievement>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @interface Export
|
||||
* UIAF 头部信息
|
||||
* @since Alpha v0.1.5
|
||||
* @description UIAF 头部信息
|
||||
* @property {string} export_app 导出的应用名称
|
||||
* @property {number} export_timestamp 导出时间戳,正确时间戳得乘以 1000
|
||||
* @property {string} export_app_version 导出的应用版本
|
||||
* @property {string} uiaf_version UIAF 版本
|
||||
* @return Export
|
||||
*/
|
||||
interface Export {
|
||||
type Export = {
|
||||
/* 导出的应用名称 */
|
||||
export_app: string;
|
||||
/* 导出时间戳,秒级 */
|
||||
export_timestamp: number;
|
||||
/* 导出的应用版本 */
|
||||
export_app_version: string;
|
||||
/* UIAF 版本 */
|
||||
uiaf_version: string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @interface Achievement
|
||||
* @since Alpha v0.1.5
|
||||
* @description UIAF 单个成就数据
|
||||
* @property {number} id 成就 ID
|
||||
* @property {number} timestamp 成就记录时间戳,正确时间戳得乘以 1000
|
||||
* @property {number} current 成就进度
|
||||
* @property {number} status 成就状态,0 为未完成,1 为已完成
|
||||
* @return Achievement
|
||||
* 成就完成状态
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
interface Achievement {
|
||||
const AchiItemStat = <const>{
|
||||
/* 无效状态 */
|
||||
Invalid: 0,
|
||||
/* 未完成 */
|
||||
Unfinished: 1,
|
||||
/* 已完成未领取奖励 */
|
||||
Finished: 2,
|
||||
/* 已领取奖励 */
|
||||
RewardTaken: 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* 成就完成状态枚举
|
||||
* @since Beta v0.7.8
|
||||
*/
|
||||
type AchiItemStatEnum = (typeof AchiItemStat)[keyof typeof AchiItemStat];
|
||||
|
||||
/**
|
||||
* 成就信息
|
||||
* @since Beta v0.7.8
|
||||
* @description UIAF 单个成就数据
|
||||
*/
|
||||
type Achievement = {
|
||||
/* 成就 ID */
|
||||
id: number;
|
||||
/* 成就记录时间戳,秒级 */
|
||||
timestamp: number;
|
||||
/* 成就进度 */
|
||||
current: number;
|
||||
status: number;
|
||||
}
|
||||
/* 成就状态 */
|
||||
status: AchiItemStatEnum;
|
||||
};
|
||||
}
|
||||
|
||||
41
src/types/Plugins/Yae.d.ts
vendored
41
src/types/Plugins/Yae.d.ts
vendored
@@ -8,44 +8,5 @@ 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;
|
||||
};
|
||||
type AchiListRes = Array<TGApp.Plugins.UIAF.Achievement>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user