fix: recursively resolve sdkserver and gameserver in subfolders, eliminate firefly proxy for GaCy PS
This commit is contained in:
1 parent
5f45841936
commit
054ab83307
4 files changed
+141
-107
No files matched your search
@@ -68,6 +68,15 @@
|
||||
* **สาเหตุ:** Git พยายามเปิด Interactive Prompt เพื่อถาม Username/Password ในเทอร์มินัลที่ไม่มี TTY
|
||||
* **แนวทางแก้ไข:** ต้องตั้งค่า `$env:GIT_TERMINAL_PROMPT = "0"` ใน PowerShell หรือ `os.Setenv("GIT_TERMINAL_PROMPT", "0")` ใน Go ก่อนเรียกคำสั่ง Git เสมอ
|
||||
|
||||
### กับดักที่ 6: โครงสร้างไฟล์ GaCy-GO (for ship/sdkserver) และการตัด Firefly Proxy
|
||||
* **อาการ:** ตัวเกม Star Rail ฟ้อง `Network connection error: Please check your network state, disable any network proxies...` และ Launcher หาเซิร์ฟเวอร์ไม่พบ
|
||||
* **สาเหตุ:**
|
||||
1. ไฟล์ Zip ของ `gacy 4.6.54.zip` แตกไฟล์ออกมามีโฟลเดอร์ย่อยชื่อ `for ship` บรรจุ `sdkserver-go.exe` หากค้นหาเฉพาะโฟลเดอร์ root จะมองไม่เห็น SDK Server ทำให้ Dispatch Server ไม่ถูกเปิด
|
||||
2. โค้ดเดิมยังคงสั่งรัน `firefly-go-proxy.exe` เข้าไปดักจับและเปลี่ยนระบบ Proxy ของ Windows ทำให้ Client ของ Star Rail ตรวจจับว่ามี Proxy แปลกปลอมและปฏิเสธการเชื่อมต่อ
|
||||
* **แนวทางแก้ไข:**
|
||||
1. ปรับปรุง `FindServerExecutables()` ใน Go ให้ค้นหาไฟล์ไบนารีแบบ **Recursive Search** ทุกโฟลเดอร์ย่อย (`for ship`, `sdkserver`) และทำ Auto-Sync ไฟล์จาก `for ship` ขึ้นมาที่ root ของ `server/`
|
||||
2. ตัดระบบ `firefly-go-proxy` ออกจากการเริ่มเล่น GaCy PS 100% โดยเรียงลำดับการเปิด: **SDK Server (Dispatch) -> รอ 800ms -> Game Server -> รอ 1000ms -> เปิดตัวเกม**
|
||||
|
||||
---
|
||||
|
||||
## 3. รายละเอียดสถาปัตยกรรมระบบอัปเดต (Isolated Updater Architecture)
|
||||
|
||||
@@ -9,14 +9,20 @@ export async function CheckUpdateServer(
|
||||
serverVersion: string
|
||||
): Promise<{ isUpdate: boolean; isExists: boolean; version: string }> {
|
||||
const [ok, latestVersion, error] = await GitService.GetLatestServerVersion()
|
||||
let isExists = await FSService.FileExists(serverPath)
|
||||
let isExists = false
|
||||
|
||||
if (!isExists) {
|
||||
const [gameServer] = await GitService.FindServerExecutables()
|
||||
if (gameServer) {
|
||||
useSettingStore.getState().setServerPath(gameServer)
|
||||
isExists = true
|
||||
}
|
||||
const [gameServer, sdkServer] = await GitService.FindServerExecutables()
|
||||
if (gameServer) {
|
||||
useSettingStore.getState().setServerPath(gameServer)
|
||||
isExists = true
|
||||
}
|
||||
if (sdkServer) {
|
||||
useSettingStore.getState().setSdkServerPath(sdkServer)
|
||||
isExists = true
|
||||
}
|
||||
|
||||
if (!isExists && serverPath) {
|
||||
isExists = await FSService.FileExists(serverPath)
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
@@ -39,7 +45,9 @@ export async function UpdateServer(serverVersion: string) : Promise<void> {
|
||||
const [gameServer, sdkServer] = await GitService.FindServerExecutables()
|
||||
setDownloadType("Download server successfully")
|
||||
setServerVersion(serverVersion)
|
||||
setServerPath(gameServer || "./server/gameserver-go.exe")
|
||||
if (gameServer) {
|
||||
setServerPath(gameServer)
|
||||
}
|
||||
if (sdkServer) {
|
||||
setSdkServerPath(sdkServer)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import useModalStore from '@/stores/modalStore';
|
||||
import useLauncherStore from '@/stores/launcherStore';
|
||||
import { motion } from 'motion/react';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import { CheckUpdateLauncher, CheckUpdateProxy, CheckUpdateServer, sleep, UpdateLauncher, UpdateProxy, UpdateServer } from '@/helper';
|
||||
import { CheckUpdateLauncher, CheckUpdateServer, sleep, UpdateLauncher, UpdateProxy, UpdateServer } from '@/helper';
|
||||
import UpdateModal from '@/components/updateModal';
|
||||
import { BackgroundSelector } from '@/components/backgroudModal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -25,11 +25,9 @@ export default function LauncherPage() {
|
||||
setServerPath,
|
||||
sdkServerPath,
|
||||
setSdkServerPath,
|
||||
proxyPath,
|
||||
gameDir,
|
||||
gameDir,
|
||||
serverVersion,
|
||||
proxyVersion,
|
||||
background,
|
||||
background,
|
||||
selectedServer,
|
||||
setSelectedServer
|
||||
} = useSettingStore()
|
||||
@@ -48,8 +46,7 @@ export default function LauncherPage() {
|
||||
downloadType,
|
||||
isDownloading,
|
||||
serverRunning,
|
||||
proxyRunning,
|
||||
gameRunning,
|
||||
gameRunning,
|
||||
progressDownload,
|
||||
downloadSpeed,
|
||||
updateData,
|
||||
@@ -60,8 +57,7 @@ export default function LauncherPage() {
|
||||
setProxyReady,
|
||||
setIsDownloading,
|
||||
setServerRunning,
|
||||
setProxyRunning,
|
||||
setGameRunning,
|
||||
setGameRunning,
|
||||
setUpdateData,
|
||||
} = useLauncherStore()
|
||||
|
||||
@@ -88,31 +84,25 @@ export default function LauncherPage() {
|
||||
|
||||
useEffect(() => {
|
||||
const check = async () => {
|
||||
if (!serverVersion || !proxyVersion) {
|
||||
setServerReady(false)
|
||||
setProxyReady(false)
|
||||
return
|
||||
const [gameServer, sdkServer] = await GitService.FindServerExecutables()
|
||||
let serverExists = false
|
||||
if (gameServer) {
|
||||
setServerPath(gameServer)
|
||||
serverExists = true
|
||||
}
|
||||
|
||||
let serverExists = await FSService.FileExists(serverPath)
|
||||
if (!serverExists) {
|
||||
const [gameServer, sdkServer] = await GitService.FindServerExecutables()
|
||||
if (gameServer) {
|
||||
setServerPath(gameServer)
|
||||
serverExists = true
|
||||
}
|
||||
if (sdkServer) {
|
||||
setSdkServerPath(sdkServer)
|
||||
}
|
||||
if (sdkServer) {
|
||||
setSdkServerPath(sdkServer)
|
||||
serverExists = true
|
||||
}
|
||||
if (!serverExists && serverPath) {
|
||||
serverExists = await FSService.FileExists(serverPath)
|
||||
}
|
||||
|
||||
const proxyExists = await FSService.FileExists(proxyPath)
|
||||
setServerReady(serverExists)
|
||||
setProxyReady(proxyExists)
|
||||
setProxyReady(true)
|
||||
}
|
||||
|
||||
check()
|
||||
}, [serverPath, proxyPath, serverVersion, proxyVersion])
|
||||
}, [serverPath, serverVersion])
|
||||
|
||||
useEffect(() => {
|
||||
const checkStartUp = async (): Promise<void> => {
|
||||
@@ -122,34 +112,30 @@ export default function LauncherPage() {
|
||||
if (launcherData.isUpdate) {
|
||||
setUpdateData({
|
||||
server: { isUpdate: false, isExists: false, version: "" },
|
||||
proxy: { isUpdate: false, isExists: false, version: "" },
|
||||
proxy: { isUpdate: false, isExists: true, version: "" },
|
||||
launcher: launcherData
|
||||
})
|
||||
setIsOpenSelfUpdateModal(true)
|
||||
return
|
||||
}
|
||||
const serverData = await CheckUpdateServer(serverPath, serverVersion)
|
||||
const proxyData = await CheckUpdateProxy(proxyPath, proxyVersion)
|
||||
setUpdateData({
|
||||
setUpdateData({
|
||||
server: serverData,
|
||||
proxy: proxyData,
|
||||
proxy: { isUpdate: false, isExists: true, version: "" },
|
||||
launcher: launcherData
|
||||
})
|
||||
|
||||
if (!serverData.isExists || !proxyData.isExists) {
|
||||
if (!serverData.isExists) {
|
||||
setServerReady(false)
|
||||
setProxyReady(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (serverData.isUpdate || proxyData.isUpdate) {
|
||||
if (serverData.isUpdate) {
|
||||
setServerReady(true)
|
||||
setProxyReady(true)
|
||||
setIsOpenUpdateDataModal(true)
|
||||
return
|
||||
}
|
||||
setServerReady(true)
|
||||
setProxyReady(true)
|
||||
}
|
||||
checkStartUp()
|
||||
}, []);
|
||||
@@ -188,7 +174,7 @@ export default function LauncherPage() {
|
||||
}
|
||||
|
||||
const handleStartGame = async () => {
|
||||
await FSService.LogMessage("INFO", "handleStartGame initiated: selectedServer=" + selectedServer + ", gamePath=" + gamePath + ", serverPath=" + serverPath + ", proxyPath=" + proxyPath)
|
||||
await FSService.LogMessage("INFO", "handleStartGame initiated: selectedServer=" + selectedServer + ", gamePath=" + gamePath + ", serverPath=" + serverPath)
|
||||
if (!gamePath) {
|
||||
await FSService.LogMessage("WARN", "handleStartGame aborted: gamePath is empty")
|
||||
toast.info("Please select the game executable first")
|
||||
@@ -205,46 +191,33 @@ export default function LauncherPage() {
|
||||
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)
|
||||
const activeSdk = sdkServer || sdkServerPath
|
||||
await FSService.LogMessage("INFO", "handleStartGame: found gameServer=" + gameServer + ", sdkServer=" + sdkServer + ", activeServer=" + activeServer + ", activeSdk=" + activeSdk)
|
||||
|
||||
if (!activeServer) {
|
||||
await FSService.LogMessage("ERROR", "handleStartGame: activeServer missing. Opening download modal.")
|
||||
if (!activeServer && !activeSdk) {
|
||||
await FSService.LogMessage("ERROR", "handleStartGame: neither gameServer nor sdkServer found. Opening download modal.")
|
||||
toast.error("Please download GaCy PS server files first")
|
||||
setIsOpenDownloadDataModal(true)
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// 2. Start SDK Server if exists
|
||||
const activeSdk = sdkServer || sdkServerPath
|
||||
// 1. Start SDK Server first (handles dispatch & login)
|
||||
if (activeSdk) {
|
||||
await FSService.LogMessage("INFO", "handleStartGame: starting SDK server via 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 FSService.LogMessage("ERROR", "handleStartGame: SDK server start failed: " + sdkErr)
|
||||
toast.error("Failed to start SDK Server: " + sdkErr)
|
||||
return
|
||||
}
|
||||
await sleep(400)
|
||||
await FSService.LogMessage("INFO", "handleStartGame: SDK server started successfully")
|
||||
await sleep(800)
|
||||
} else {
|
||||
await FSService.LogMessage("WARN", "handleStartGame: activeSdk not found! Proceeding with gameServer only.")
|
||||
}
|
||||
|
||||
// 3. Start Game Server
|
||||
if (!serverRunning) {
|
||||
// 2. Start Game Server second
|
||||
if (activeServer && !serverRunning) {
|
||||
await FSService.LogMessage("INFO", "handleStartGame: starting Game server via StartWithConsole(" + activeServer + ")")
|
||||
const [resultServer, error] = await FSService.StartWithConsole(activeServer)
|
||||
if (!resultServer) {
|
||||
@@ -254,10 +227,10 @@ export default function LauncherPage() {
|
||||
}
|
||||
await FSService.LogMessage("INFO", "handleStartGame: Game server started successfully")
|
||||
setServerRunning(true)
|
||||
await sleep(1000)
|
||||
}
|
||||
await sleep(1000)
|
||||
} else {
|
||||
await FSService.LogMessage("INFO", "handleStartGame: running in Official Server mode (no proxy/PS)")
|
||||
await FSService.LogMessage("INFO", "handleStartGame: running in Official Server mode (no PS)")
|
||||
}
|
||||
|
||||
// 4. Start Game
|
||||
@@ -473,18 +446,17 @@ export default function LauncherPage() {
|
||||
<button
|
||||
onClick={async () => {
|
||||
const serverData = await CheckUpdateServer(serverPath, serverVersion)
|
||||
const proxyData = await CheckUpdateProxy(proxyPath, proxyVersion)
|
||||
setUpdateData({
|
||||
server: serverData,
|
||||
proxy: proxyData,
|
||||
proxy: { isUpdate: false, isExists: true, version: "" },
|
||||
launcher: updateData.launcher
|
||||
})
|
||||
|
||||
if (!serverData.isExists || !proxyData.isExists) {
|
||||
if (!serverData.isExists) {
|
||||
setIsOpenDownloadDataModal(true)
|
||||
return
|
||||
}
|
||||
if (serverData.isUpdate || proxyData.isUpdate) {
|
||||
if (serverData.isUpdate) {
|
||||
setIsOpenUpdateDataModal(true)
|
||||
return
|
||||
}
|
||||
@@ -501,14 +473,6 @@ export default function LauncherPage() {
|
||||
{t("home.menu_open_server")}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button onClick={() => {
|
||||
FSService.OpenFolder("./proxy")
|
||||
}}
|
||||
>
|
||||
{t("home.menu_open_proxy")}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button disabled={!gameDir} onClick={() => {
|
||||
if (gameDir) {
|
||||
@@ -546,9 +510,7 @@ export default function LauncherPage() {
|
||||
|
||||
{/* Downloading Progress Bar */}
|
||||
{isDownloading && (
|
||||
updateData.proxy.isUpdate
|
||||
|| updateData.server.isUpdate
|
||||
|| !updateData.proxy.isExists
|
||||
updateData.server.isUpdate
|
||||
|| !updateData.server.isExists
|
||||
) && (
|
||||
<div className="fixed bottom-4 left-1/2 transform -translate-x-1/2 z-10 w-[60vw] bg-black/60 backdrop-blur-md rounded-2xl p-4 shadow-2xl border border-white/10">
|
||||
|
||||
@@ -119,6 +119,7 @@ func (g *GitService) DownloadServerProgress(version string) (bool, string) {
|
||||
return false, fmt.Sprintf("unzip server: %v", err)
|
||||
}
|
||||
os.Remove(saveZipPath)
|
||||
g.organizeExtractedServer(constant.ServerStorageUrl)
|
||||
return true, ""
|
||||
}
|
||||
|
||||
@@ -180,30 +181,84 @@ func (g *GitService) UnzipServer() {
|
||||
_ = os.Remove(zipPath)
|
||||
}
|
||||
}
|
||||
g.organizeExtractedServer(constant.ServerStorageUrl)
|
||||
}
|
||||
|
||||
func (g *GitService) organizeExtractedServer(dest string) {
|
||||
forShipPath := filepath.Join(dest, "for ship")
|
||||
if info, err := os.Stat(forShipPath); err == nil && info.IsDir() {
|
||||
logger.Info("organizeExtractedServer: found 'for ship' subdirectory in %s, synchronizing files", dest)
|
||||
entries, err := os.ReadDir(forShipPath)
|
||||
if err == nil {
|
||||
for _, entry := range entries {
|
||||
src := filepath.Join(forShipPath, entry.Name())
|
||||
dst := filepath.Join(dest, entry.Name())
|
||||
if !entry.IsDir() {
|
||||
if _, err := os.Stat(dst); os.IsNotExist(err) {
|
||||
data, readErr := os.ReadFile(src)
|
||||
if readErr == nil {
|
||||
_ = os.WriteFile(dst, data, 0755)
|
||||
logger.Info("organizeExtractedServer: copied %s -> %s", entry.Name(), dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GitService) FindServerExecutables() (string, string) {
|
||||
serverDir := constant.ServerStorageUrl
|
||||
var candidateRoots []string
|
||||
|
||||
if exePath, err := os.Executable(); err == nil {
|
||||
exeDir := filepath.Dir(exePath)
|
||||
candidateRoots = append(candidateRoots, filepath.Join(exeDir, "server"))
|
||||
candidateRoots = append(candidateRoots, filepath.Join(filepath.Dir(exeDir), "server"))
|
||||
}
|
||||
candidateRoots = append(candidateRoots, constant.ServerStorageUrl, "./bin/server", "server", "bin/server")
|
||||
|
||||
var gameServer, sdkServer string
|
||||
seen := make(map[string]bool)
|
||||
|
||||
// Candidate filenames for game server
|
||||
for _, name := range []string{"gameserver-go.exe", "server.exe", "gameserver.exe", "firefly-go_win.exe"} {
|
||||
p := filepath.Join(serverDir, name)
|
||||
if info, err := os.Stat(p); err == nil && !info.IsDir() {
|
||||
gameServer = p
|
||||
for _, root := range candidateRoots {
|
||||
absRoot, err := filepath.Abs(root)
|
||||
if err != nil || seen[absRoot] {
|
||||
continue
|
||||
}
|
||||
seen[absRoot] = true
|
||||
|
||||
if info, err := os.Stat(absRoot); err != nil || !info.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
_ = filepath.Walk(absRoot, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
nameLower := strings.ToLower(info.Name())
|
||||
|
||||
// Search SDK server
|
||||
if sdkServer == "" {
|
||||
if nameLower == "sdkserver-go.exe" || nameLower == "sdkserver.exe" || (strings.Contains(nameLower, "sdkserver") && strings.HasSuffix(nameLower, ".exe")) {
|
||||
sdkServer = path
|
||||
}
|
||||
}
|
||||
|
||||
// Search Game server
|
||||
if gameServer == "" {
|
||||
if nameLower == "gameserver-go.exe" || nameLower == "server.exe" || nameLower == "gameserver.exe" || nameLower == "firefly-go_win.exe" || (strings.Contains(nameLower, "gameserver") && strings.HasSuffix(nameLower, ".exe")) {
|
||||
gameServer = path
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if gameServer != "" && sdkServer != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Candidate filenames for sdk server
|
||||
for _, name := range []string{"sdkserver-go.exe", "sdkserver.exe"} {
|
||||
p := filepath.Join(serverDir, name)
|
||||
if info, err := os.Stat(p); err == nil && !info.IsDir() {
|
||||
sdkServer = p
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("FindServerExecutables: serverDir=%s, found gameServer=%s, sdkServer=%s", serverDir, gameServer, sdkServer)
|
||||
logger.Info("FindServerExecutables: found gameServer=%s, sdkServer=%s", gameServer, sdkServer)
|
||||
return gameServer, sdkServer
|
||||
}
|
||||
Reference in new issue
Block a user