feat: add comprehensive diagnostic logging subsystem and UI controls

This commit is contained in:
CAtlas0784 committed 2026-10-30 23:08:23 +07:00
1 parent 517436321b
commit 5f45841936
15 files changed
+495 -23

No files matched your search

@@ -39,6 +39,14 @@ export function GetDir(path) {
return $Call.ByID(1567262998, path);
}
/**
* GetLogFilePath returns the absolute path of launcher.log
* @returns {$CancellablePromise<string>}
*/
export function GetLogFilePath() {
return $Call.ByID(4150210098);
}
/**
* @returns {$CancellablePromise<[string, string]>}
*/
@@ -54,6 +62,16 @@ export function Join(...paths) {
return $Call.ByID(3036412633, paths);
}
/**
* LogMessage receives log entries from the frontend and appends them to launcher.log
* @param {string} level
* @param {string} message
* @returns {$CancellablePromise<void>}
*/
export function LogMessage(level, message) {
return $Call.ByID(4279191144, level, message);
}
/**
* @param {string} path
* @returns {$CancellablePromise<[boolean, string]>}
@@ -62,6 +80,14 @@ export function OpenFolder(path) {
return $Call.ByID(1601714793, path);
}
/**
* OpenLogsFolder opens the folder containing launcher.log in Windows File Explorer
* @returns {$CancellablePromise<[boolean, string]>}
*/
export function OpenLogsFolder() {
return $Call.ByID(2156513786);
}
/**
* @param {string} filter
* @returns {$CancellablePromise<string>}
@@ -86,6 +112,15 @@ export function ReadConfigFile(gameDir, fileName) {
return $Call.ByID(3834168665, gameDir, fileName);
}
/**
* ReadLogs returns the last maxLines of the log file
* @param {number} maxLines
* @returns {$CancellablePromise<string>}
*/
export function ReadLogs(maxLines) {
return $Call.ByID(2698372806, maxLines);
}
/**
* @param {string} path
* @returns {$CancellablePromise<void>}
+1
View File
@@ -61,6 +61,7 @@
"menu_open_server": "Open server folder",
"menu_open_proxy": "Open proxy folder",
"menu_open_voice": "Open voice folder",
"menu_open_logs": "Open logs folder",
"status_updating_launcher": "Updating launcher",
"status_update_success": "Launcher updated successfully, auto closing after 5s",
"status_update_failed": "Launcher update failed, auto closing after 5s",
+1
View File
@@ -61,6 +61,7 @@
"menu_open_server": "サーバーフォルダを開く",
"menu_open_proxy": "プロキシフォルダを開く",
"menu_open_voice": "ボイスフォルダを開く",
"menu_open_logs": "ログフォルダを開く",
"status_updating_launcher": "ランチャーを更新中",
"status_update_success": "更新完了。5秒後に閉じます",
"status_update_failed": "更新失敗。5秒後に閉じます",
+1
View File
@@ -61,6 +61,7 @@
"menu_open_server": "서버 폴더 열기",
"menu_open_proxy": "프록시 폴더 열기",
"menu_open_voice": "음성 폴더 열기",
"menu_open_logs": "로그 폴더 열기",
"status_updating_launcher": "런처 업데이트 중",
"status_update_success": "업데이트 완료, 5초 후 종료",
"status_update_failed": "업데이트 실패, 5초 후 종료",
+1
View File
@@ -61,6 +61,7 @@
"menu_open_server": "เปิดโฟลเดอร์เซิร์ฟเวอร์",
"menu_open_proxy": "เปิดโฟลเดอร์พร็อกซี",
"menu_open_voice": "เปิดโฟลเดอร์เสียงพากย์",
"menu_open_logs": "เปิดโฟลเดอร์ Log",
"status_updating_launcher": "กำลังอัปเดต Launcher",
"status_update_success": "อัปเดต Launcher สำเร็จ ปิดอัตโนมัติใน 5 วินาที",
"status_update_failed": "อัปเดต Launcher ไม่สำเร็จ ปิดอัตโนมัติใน 5 วินาที",
+1
View File
@@ -61,6 +61,7 @@
"menu_open_server": "Mở thư mục Server",
"menu_open_proxy": "Mở thư mục Proxy",
"menu_open_voice": "Mở thư mục Voice",
"menu_open_logs": "Mở thư mục nhật ký",
"status_updating_launcher": "Đang cập nhật Launcher",
"status_update_success": "Cập nhật thành công, tự động đóng sau 5s",
"status_update_failed": "Cập nhật thất bại, tự động đóng sau 5s",
+1
View File
@@ -61,6 +61,7 @@
"menu_open_server": "打开服务器文件夹",
"menu_open_proxy": "打开代理文件夹",
"menu_open_voice": "打开语音文件夹",
"menu_open_logs": "打开日志文件夹",
"status_updating_launcher": "正在更新启动器",
"status_update_success": "更新成功,5秒后自动关闭",
"status_update_failed": "更新失败,5秒后自动关闭",
+41 -1
View File
@@ -2,6 +2,7 @@ import { CheckUpdateLauncher } from "@/helper"
import useModalStore from "@/stores/modalStore"
import useSettingStore from "@/stores/settingStore"
import useLauncherStore from "@/stores/launcherStore"
import { FSService } from "@bindings/g-launcher/internal/fs-service"
import { toast } from "react-toastify"
import { useTranslation } from "react-i18next"
@@ -91,8 +92,47 @@ export default function SettingModal({
</label>
</div>
{/* Section 3: Diagnostic Logs */}
<div className="p-4 bg-base-200 rounded-xl border border-cyan-500/40 shadow-sm">
<h4 className="font-bold text-lg mb-1 flex items-center gap-2">
<span>📋</span>
<span>Diagnostic Logs</span>
</h4>
<p className="text-xs text-white/70 mb-3">
เปิดโฟลเดอร์หรือคัดลอก Log เพื่อส่งให้ทีมพัฒนาช่วยวิเคราะห์ปัญหาการเข้าเกม
</p>
<div className="flex flex-wrap gap-2">
<button
className="btn btn-sm btn-info text-white shadow"
onClick={async () => {
const [ok, err] = await FSService.OpenLogsFolder()
if (!ok) toast.error("Failed to open logs: " + err)
}}
>
📂 เปิดโฟลเดอร์ Log
</button>
<button
className="btn btn-sm btn-outline btn-secondary"
onClick={async () => {
try {
const [logs, err] = await FSService.ReadLogs(200)
if (err) {
toast.error("Failed to read logs: " + err)
return
}
await navigator.clipboard.writeText(logs)
toast.success("คัดลอก Log 200 บรรทัดล่าสุดเรียบร้อย!")
} catch (e: any) {
toast.error("Cannot copy logs: " + e)
}
}}
>
📋 คัดลอก Log ล่าสุด
</button>
</div>
</div>
{/* Section 3: Launcher Version */}
{/* Section 4: Launcher Version */}
<div className="p-4 bg-base-200 rounded-xl border border-purple-300 shadow-sm">
<h4 className="font-bold text-lg mb-2">{t("setting.version_label")}</h4>
<div className="flex flex-wrap gap-2">
+48 -4
View File
@@ -158,22 +158,29 @@ export default function LauncherPage() {
try {
setIsLoading(true)
const basePath = await FSService.PickFile("exe")
await FSService.LogMessage("INFO", "handlePickFile: selected file=" + basePath)
if (basePath.endsWith("StarRail.exe") || basePath.endsWith("launcher.exe")) {
const normalized = basePath.replace(/\\/g, '/')
const normalized = basePath.replace(/\\/g, "/")
const folderPath = path.dirname(normalized)
const fullPath = `${folderPath}/StarRail_Data/StreamingAssets/DesignData/Windows`
const fullPath = folderPath + "/StarRail_Data/StreamingAssets/DesignData/Windows"
const exists = await FSService.DirExists(fullPath)
await FSService.LogMessage("INFO", "handlePickFile: folderPath=" + folderPath + ", DesignData exists=" + exists)
if (!exists) {
await FSService.LogMessage("WARN", "handlePickFile: DesignData not found at " + fullPath)
toast.error(t("home.error_game_dir"))
} else {
setGamePath(basePath)
setGameDir(folderPath)
await FSService.SaveGameSelection(basePath, folderPath)
toast.success(t("home.game_path_success"))
}
} else {
await FSService.LogMessage("WARN", "handlePickFile: invalid file type: " + basePath)
toast.error(t("home.error_file_type"))
}
} catch (err: any) {
const errStr = err?.stack || err?.message || String(err)
await FSService.LogMessage("ERROR", "handlePickFile error: " + errStr)
toast.error(t("home.toast_pick_folder_error"), err)
} finally {
setIsLoading(false)
@@ -181,21 +188,27 @@ export default function LauncherPage() {
}
const handleStartGame = async () => {
await FSService.LogMessage("INFO", "handleStartGame initiated: selectedServer=" + selectedServer + ", gamePath=" + gamePath + ", serverPath=" + serverPath + ", proxyPath=" + proxyPath)
if (!gamePath) {
await FSService.LogMessage("WARN", "handleStartGame aborted: gamePath is empty")
toast.info("Please select the game executable first")
return
}
if (gameRunning) {
await FSService.LogMessage("WARN", "handleStartGame aborted: game is already marked as running")
return
}
try {
setIsLoading(true)
if (selectedServer === "gacy-ps") {
await FSService.LogMessage("INFO", "handleStartGame: querying GaCy PS server executables...")
const [gameServer, sdkServer] = await GitService.FindServerExecutables()
const activeServer = gameServer || serverPath
await FSService.LogMessage("INFO", "handleStartGame: found gameServer=" + gameServer + ", sdkServer=" + sdkServer + ", activeServer=" + activeServer)
if (!activeServer) {
await FSService.LogMessage("ERROR", "handleStartGame: activeServer missing. Opening download modal.")
toast.error("Please download GaCy PS server files first")
setIsOpenDownloadDataModal(true)
return
@@ -203,12 +216,16 @@ export default function LauncherPage() {
// 1. Start Proxy
const activeProxy = proxyPath || "./proxy/firefly-go-proxy.exe"
await FSService.LogMessage("INFO", "handleStartGame: activeProxy=" + activeProxy + ", proxyRunning=" + proxyRunning)
if (!proxyRunning && !gamePath.endsWith("launcher.exe")) {
await FSService.LogMessage("INFO", "handleStartGame: starting proxy via StartWithConsole(" + activeProxy + ")")
const [resultProxy, error] = await FSService.StartWithConsole(activeProxy)
if (!resultProxy) {
await FSService.LogMessage("ERROR", "handleStartGame: proxy start failed: " + error)
toast.error(t("home.toast_start_proxy_failed") + ": " + error)
return
}
await FSService.LogMessage("INFO", "handleStartGame: proxy started successfully")
setProxyRunning(true)
}
await sleep(500)
@@ -216,39 +233,56 @@ export default function LauncherPage() {
// 2. Start SDK Server if exists
const activeSdk = sdkServer || sdkServerPath
if (activeSdk) {
await FSService.StartWithConsole(activeSdk)
await FSService.LogMessage("INFO", "handleStartGame: starting SDK server via StartWithConsole(" + activeSdk + ")")
const [resultSdk, sdkErr] = await FSService.StartWithConsole(activeSdk)
if (!resultSdk) {
await FSService.LogMessage("WARN", "handleStartGame: SDK server start warning: " + sdkErr)
} else {
await FSService.LogMessage("INFO", "handleStartGame: SDK server started successfully")
}
await sleep(400)
}
// 3. Start Game Server
if (!serverRunning) {
await FSService.LogMessage("INFO", "handleStartGame: starting Game server via StartWithConsole(" + activeServer + ")")
const [resultServer, error] = await FSService.StartWithConsole(activeServer)
if (!resultServer) {
await FSService.LogMessage("ERROR", "handleStartGame: Game server start failed: " + error)
toast.error(t("home.toast_start_server_failed") + ": " + error)
return
}
await FSService.LogMessage("INFO", "handleStartGame: Game server started successfully")
setServerRunning(true)
}
await sleep(1000)
} else {
await FSService.LogMessage("INFO", "handleStartGame: running in Official Server mode (no proxy/PS)")
}
// 4. Start Game
await FSService.LogMessage("INFO", "handleStartGame: starting game binary: " + gamePath)
if (gamePath.endsWith("launcher.exe")) {
const [resultGame, error] = await FSService.StartWithConsole(gamePath)
if (!resultGame) {
await FSService.LogMessage("ERROR", "handleStartGame: StartWithConsole game failed: " + error)
toast.error(t("home.toast_start_game_failed") + ": " + error)
return
}
} else {
const [resultGame, error] = await FSService.StartApp(gamePath)
if (!resultGame) {
await FSService.LogMessage("ERROR", "handleStartGame: StartApp game failed: " + error)
toast.error(t("home.toast_start_game_failed") + ": " + error)
return
}
}
await FSService.LogMessage("INFO", "handleStartGame: Game process started successfully!")
setGameRunning(true)
} catch (err: any) {
const errStr = err?.stack || err?.message || String(err)
await FSService.LogMessage("ERROR", "handleStartGame uncaught exception: " + errStr)
console.log(err)
toast.error(t("home.toast_start_game_error"), err)
} finally {
@@ -256,7 +290,6 @@ export default function LauncherPage() {
}
}
const handlerUpdateData = async () => {
setIsDownloading(true)
if (updateData.launcher.isUpdate) {
@@ -486,6 +519,17 @@ export default function LauncherPage() {
{t("home.menu_open_voice")}
</button>
</li>
<li>
<button onClick={async () => {
const [ok, err] = await FSService.OpenLogsFolder()
if (!ok) {
toast.error("Failed to open logs: " + err)
}
}}
>
📋 {t("home.menu_open_logs") || "Open Logs"}
</button>
</li>
</ul>
</div>
</div>