GaC hkrpg GO
เซิร์ฟเวอร์จำลอง Honkai: Star Rail ภาษา Go พอร์ตจาก GaC-hkrpg-ps และอ้างอิงการทำงานจาก PearlSR ประกอบด้วย game server, SDK/dispatch server และ REST API สำหรับ GaC-Srtool
โปรเจกต์อยู่ระหว่างพัฒนา โดยกำลังตรวจการใช้งานกับ Client 4.6.53 OS ตารางคำสั่งและ handler ที่แก้ล่าสุดอ้างอิง PearlSR commit 005d2c12db4781071f4b5b68e8f364587886a710 รายละเอียดอยู่ใน บันทึกอัปเดต protocol การตรงกันของ schema ไม่ได้ยืนยันว่าเกมทุกระบบทำงานครบ
CW — กำลังพัฒนา
กำลังทำ Currency Wars ให้ครบวงจร รายละเอียดและขอบเขตที่ยังไม่เสร็จอยู่ใน บันทึก CW ขณะนี้เพิ่ม catalog จาก Client, ข้อมูลเริ่มต้น GridFightGetData และรายการกิจกรรมแล้ว ยังไม่ถือว่า CW เล่นได้ครบ และระบบเซฟรอบ/ร้าน/เส้นทาง/การต่อสู้/ผลจบ CW ยังอยู่ระหว่างทำ
ทางเข้า CW ใช้ start anchor จาก MapEntrance และ RuntimeGroup ของ Client แทนการเลือกเสาวาร์ปแรกในแมพ ผู้ใช้ยืนยันว่าเข้าห้อง เห็นโฮโลแกรม และเปิด UI ค้างไว้ได้แล้ว ข้อมูลอยู่ใน resources/CurrencyWarsCatalog.json พร้อมที่มา ส่วนเริ่มรอบและเล่น CW ให้จบยังอยู่ระหว่างทำ
Catalog มีตารางเส้นทางจาก Client ครบชุดที่จับได้ 493 แถว ตัวอ่านตรวจรหัสเส้นทาง/บท/ห้องซ้ำและเส้นทางฝึกสอนที่ constant อ้างถึง โดยไม่ใช้ NodeTemplateID แทน StageID ของ Battle การส่งผลเริ่มรอบยังต้องต่อข้อมูลห้อง ร้านค้า และสถานะผู้เล่น CW ให้ครบก่อน
โครงสร้างเซฟ CW แยกตาม UID อยู่ใน persistent.currency_wars พร้อม revision ป้องกัน session เก่าเขียนทับ และตรวจ Client/assembly ก่อนคืนข้อมูล GridFightGetData จะอ่านเซฟเมื่อมี โดยไม่เขียนไฟล์ระหว่างอ่าน StartGamePlay บันทึกสถานะเริ่มรอบก่อนตอบกลับแล้ว พร้อม Resume/QuitLeave แต่ยังไม่ได้ยืนยันการเล่นต่อจากเซฟใน Client
ส่วนร้านค้ามีโค้ดกองการ์ดและแผนซื้อ/รีเฟรชแล้ว โดยอ่านจำนวนการ์ดกับกฎพื้นฐานจาก catalog และยังต้องเชื่อมกับกระดาน สถานะรอบ และแพ็กเก็ตคำสั่งจริง เพิ่มสถานะเริ่มต้นที่จับจาก March7 PS สำหรับด่านแรกของ Standard Gambit/Overclock ใน resources/CurrencyWarsInitialRuns.json แล้ว แต่ยังไม่ได้ต่อคำสั่งเลือกประตู ร้านค้า และการต่อสู้ครบ สถานะเริ่มต้นใช้ตัวอย่างที่จับไว้ ไม่ใช่การสุ่มรอบใหม่ทั้งหมด
โปรไฟล์เริ่ม CW ใช้นโยบาย freeplay_unlocked ใน resource ดังกล่าว ตามข้อมูล tutorial ของ March7 PS เพื่อไม่ให้ Client ส่งคำขอฝึกสอน Division 0 มายัง handler รอบปกติ นโยบายนี้ข้ามบทฝึกสอนเพื่อเข้า Freeplay โดยไม่แจกผลรางวัลบทฝึกสอน ผู้ใช้ยืนยันว่าเข้า Investment Environment และผ่านไปหน้า Preparation ได้แล้ว
ผู้ใช้ยืนยันแล้วว่าเข้า CW ถึงหน้า Investment Environment ได้ เพิ่มการตอบ Confirm จาก GridFightHandleBonfireHealHpCsReq ไปยัง GridFightHandlePendingActionScRsp พร้อม sync สถานะและบันทึกรอบก่อนตอบกลับ ขณะนี้ใช้ผลเริ่มต้นที่จับจาก reference สำหรับสามตัวเลือกของด่านแรกทั้งสองโหมดใน resources/CurrencyWarsEntryTransitions.json ยังเป็นผลเริ่มต้นคงที่ และยังไม่ได้ต่อคำสั่งรอบถัดไป/ร้าน/การต่อสู้ครบ ค่าเอฟเฟกต์บางอย่างของ reference ต่างจาก Client ปัจจุบัน จึงยังไม่ถือว่าเอฟเฟกต์หรือ CW เล่นครบแล้ว
Unstuck จาก GaC srtool
ปุ่ม Unstuck ใน GM Command Console ส่ง POST /api/client/unstuck ให้เกมเรียกฟังก์ชันปลดการบล็อกขณะรอแพ็กเก็ตใน heartbeat ถัดไป เว็บรอผลยืนยันจาก Client ผ่าน /api/client/unstuck/result ก่อนแสดงว่าสำเร็จ ใช้คำสั่ง /unstuck ใน console ได้ด้วย การกู้ UI นี้ไม่ลบเซฟ CW ไม่ย้ายแมพ และไม่สร้างผลสำเร็จปลอมให้คำสั่งเล่นเกม หากเซิร์ฟเวอร์หรือเกมไม่เชื่อมต่อจะยังปลดไม่ได้ และยังต้องแก้ handler ที่ทำให้ค้างด้วย
ตัวอ่าน catalog รองรับตัวละครเริ่มต้นจากสมุด 60 รายการและค่าด่านจากโปรไฟล์ใหม่ 71 รายการ พร้อมตรวจราคาขายเริ่มต้นกับตารางดาว/ความหายาก ข้อมูลนี้ยังไม่ครอบคลุมตัวแปรทั้งหมดของร้าน และค่าด่านยังต้องคำนวณผลของ talents/modifiers เพิ่ม จึงยังไม่ใช่ระบบร้านค้าหรือผลรางวัล CW ที่เสร็จแล้ว
ข้อมูลเริ่มต้นของช่วงคะแนน CycleScoreRewardGetData ใช้เวลาจบจาก activity_schedule.end_time เพื่อไม่ให้ตัวนับเวลาได้ค่าเริ่มต้นปี 1970 ขณะนี้ยังเป็นช่วงเปิดเล่นตาม config ระบบรีเซ็ตรายสัปดาห์ คะแนนสะสม และการรับรางวัลยังอยู่ระหว่างพัฒนา
ช่วงเวลากิจกรรมใช้ activity_schedule.begin_time และ activity_schedule.end_time ใน server.json (Unix seconds) override ด้วย server.local.json หรือ GAC_ACTIVITY_BEGIN_TIME / GAC_ACTIVITY_END_TIME ได้ ต้องมีค่าทั้งคู่ โดยเริ่มไม่น้อยกว่า 0 และสิ้นสุดมากกว่าเริ่ม SDK, gateway และ launcher ตรวจค่าเดียวกัน ช่วงเวลานี้เป็นนโยบายเปิดเล่นของเซิร์ฟเวอร์ ไม่ใช่กำหนดการทางการของเกม
เริ่มใช้งานบน Windows
ข้อมูลตัวละครหายจนค้างหน้าโหลด
หาก Player.log มี TeamManager._CreateMemberEntity / IndexOutOfRangeException ให้ตรวจว่า freesr-data.json ยังมี avatars และครบทุกรหัสในทีมปัจจุบัน การนำเข้าเฉพาะ relic/light cone ต้องไม่ลบส่วนตัวละครออก เซิร์ฟเวอร์จะปฏิเสธ roster ว่างและทีมที่อ้างถึงตัวละครที่ไม่มี พร้อมเก็บข้อมูล session เดิมไว้เมื่อ hot reload พบไฟล์ผิด
POST /api/freesr/data รวมเฉพาะฟิลด์ที่ส่งมาและรวมรายการ avatar ตามรหัส ส่วน PUT ใช้แทนข้อมูลทั้งชุดและต้องมี avatars, lightcones, relics, battle_config ครบ การบันทึกตรวจ roster/ทีมก่อนเขียนแบบ atomic และไม่เขียน persistent กลับจากการอ่านข้อมูล ใช้ SDK และ Game server build ที่มีการป้องกันนี้ทั้งคู่
- ใช้ Windows กับ PowerShell และอินเทอร์เน็ตสำหรับ build ครั้งแรก Launcher ตรวจ Go ตามเวอร์ชันขั้นต่ำใน
go.mod; ถ้าไม่มีหรือเก่าเกินไป จะดาวน์โหลด ZIP จากรายการรุ่นทางการ ตรวจ SHA-256 แล้วติดตั้งในlauncher.go_install.directoryโดยไม่ต้องใช้สิทธิ์ผู้ดูแล ดู การติดตั้ง Go และ รายการไฟล์ทางการ - เตรียม Client, launcher/proxy และการ redirect dispatch ให้เชื่อมต่อเซิร์ฟเวอร์ที่กำหนดใน
server.json - เก็บไฟล์ข้อมูลและโฟลเดอร์
resourcesไว้ใน root ของโปรเจกต์ และตั้งค่า launcher ตามหัวข้อ config ด้านล่าง - เปิด
run.batแล้วเลือกเมนูที่ต้องการ
| เมนู | การทำงาน |
|---|---|
1 หรือ Enter |
Build ทั้งสองเซิร์ฟเวอร์ แล้วเปิดเซิร์ฟเวอร์และเกม |
2 |
เปิดทั้งสองเซิร์ฟเวอร์จาก .exe ที่มีอยู่ |
3 |
เปิดเฉพาะ SDK server |
4 |
เปิดเฉพาะ game server |
5 |
เปิดทั้งสองเซิร์ฟเวอร์จาก .exe ที่มีอยู่ และเรียก Gay Proxy |
6 |
Build ทั้งสอง .exe อย่างเดียว |
7 |
ออก |
8 หรือ stop |
หยุดเซิร์ฟเวอร์ของโปรเจกต์นี้ |
เมนู 1 และ 6 หยุดเมื่อ build ไม่ผ่าน ส่วนเมนู 2–5 ใช้ binary ที่มีอยู่ หลังแก้ซอร์สจึงต้อง build ใหม่ก่อนใช้เมนูเหล่านั้น
run.bat เรียก run.ps1 ผ่าน PowerShell โดยอ่าน path เกม/Proxy จาก config ไม่มีการเดา Desktop, ไดรฟ์ หรือชื่อโฟลเดอร์ตามเวอร์ชัน ต้องตั้ง launcher.client_path เป็นไฟล์ launcher/เกมที่จะเปิด และ launcher.proxy_path เป็นไฟล์ Proxy ที่ต้องการ ถ้าเว้นว่างจะเปิดเซิร์ฟเวอร์และแสดงวิธีตั้งค่า
SDK และ game server เปิดหน้าต่างคอนโซลแยกกันให้เห็น log โดยตรง หลังทำเมนูที่เลือกเสร็จ หน้าต่าง run.bat จะปิดเองใน 5 วินาที ส่วนหน้าต่างเซิร์ฟเวอร์ยังเปิดอยู่ ใช้ run.bat stop เพื่อหยุด หรือปิดหน้าต่างเซิร์ฟเวอร์ที่ต้องการ สคริปต์หยุดเฉพาะ process ที่ path executable ตรงกับ root นี้ ก่อนเริ่มใหม่หรือแทนที่ binary ที่ build สำเร็จ การเรียกด้วย argument เช่น run.bat build จะคืนค่าทันทีเมื่อทำงานเสร็จโดยไม่รอ 5 วินาที
Config และกฎห้าม hardcode
AGENTS.md กำหนดให้ค่าที่เปลี่ยนตามเครื่อง/deployment/เวอร์ชันต้องมาจาก config, environment หรือ resource ห้ามฝัง path, IP, port หรือ fallback เฉพาะเครื่องใน source และ startup scripts
ลำดับความสำคัญ: environment > local config > config หลัก
server.json: ค่าเครือข่ายและเวอร์ชันที่แชร์กันระหว่าง launcher, game server และ SDK serverserver.local.json: override เฉพาะเครื่อง ไฟล์นี้ถูก ignore โดย Git คัดลอกserver.local.example.jsonแล้วใส่ path ของเครื่องได้GAC_CONFIG: เลือกไฟล์หลักอื่น โดย local override ใช้ชื่อเดียวกันลงท้าย.local.json- path แบบ relative ใน launcher อ้างอิง root ของโปรเจกต์
| ค่าใน config | Environment override | ใช้สำหรับ |
|---|---|---|
account_name |
GAC_ACCOUNT_NAME |
username ในคำตอบ SDK login และชื่อผู้เล่นในเกม ใช้ค่าเดียวกัน |
player_uid |
GAC_PLAYER_UID |
UID ผู้เล่น ใช้ร่วมกันทั้ง SDK และ game server; ต้องเป็นจำนวนเต็ม 1–4294967295 |
client_version |
GAC_CLIENT_VERSION |
เวอร์ชันที่แสดงใน log/API |
game_host, game_port |
GAC_GAME_HOST, GAC_GAME_PORT |
ที่อยู่ bind ของ game server |
sdk_host, sdk_port |
GAC_SDK_HOST, GAC_SDK_PORT |
ที่อยู่ bind ของ SDK server |
public_game_host |
GAC_PUBLIC_GAME_HOST |
ที่อยู่ game server ที่ gateway แจ้ง Client |
public_sdk_url |
GAC_PUBLIC_SDK_URL |
Base URL ที่ dispatch แจ้ง Client |
battle_diagnostics.live_report_stale_seconds |
GAC_LIVE_BATTLE_STALE_SECONDS |
อายุสูงสุดของ snapshot Live ก่อนกลับไปใช้รายงานที่บันทึกแล้ว |
launcher.client_path |
GAC_CLIENT_PATH |
ไฟล์ launcher/เกมที่จะเปิด |
launcher.proxy_path |
GAC_PROXY_PATH |
ไฟล์ Proxy ที่จะเปิด |
launcher.go_command |
GAC_GO_COMMAND |
คำสั่ง Go จาก PATH หรือ path ที่กำหนด |
เปลี่ยน username และ UID ได้ที่เดียวใน server.json: account_name คือชื่อทั้งบัญชีและในเกม ส่วน player_uid คือ UID (ปัจจุบัน 52022) ถ้าต้องการค่าเฉพาะเครื่อง ให้ใส่สองคีย์นี้ใน server.local.json แทน หลังแก้ให้รีสตาร์ตทั้ง SDK และ game server แล้วออกจากบัญชีและ login ใหม่เพื่ออัปเดตชื่อที่ Client cache ไว้ ไม่ต้องแก้ซอร์สหรือไฟล์เซฟเพื่อเปลี่ยนสองค่านี้
run.bat requirements เตรียม Go อย่างเดียว ส่วนเมนู build เรียกขั้นตอนนี้อัตโนมัติ การรัน executable ที่ build แล้วไม่ต้องมี Go ติดตั้งในเครื่อง ตั้ง launcher.go_install.enabled=false เพื่อใช้ Go ที่ติดตั้งเองเท่านั้น URL ของ manifest/ดาวน์โหลดและโฟลเดอร์ติดตั้งอยู่ใน server.json และ override ผ่านไฟล์ local ได้ ไม่มี path โปรแกรมเฉพาะเครื่องฝังใน launcher โฟลเดอร์ .runtime/ และ receipt ของ toolchain ไม่ถูก commit
เมื่อเปลี่ยนเครือข่ายให้ตั้งทั้ง bind และค่าที่ประกาศให้ Client เข้าถึงได้ โดยเฉพาะ port ใน public_sdk_url เมื่อ SDK รับการเชื่อมต่อโดยตรง ค่าเหล่านี้มีค่าเริ่มต้นอยู่ในไฟล์ config และไม่มี fallback เครือข่ายในโค้ด ต้องรีสตาร์ตเซิร์ฟเวอร์หลังเปลี่ยน config การเปลี่ยน client_version ไม่ได้แปลง protocol หรือทรัพยากรของเกมให้อัตโนมัติ
ตรวจ config โดยไม่เปิดเกมหรือเซิร์ฟเวอร์:
.\run.bat check
มอนในฉากและการเชื่อมต่อ Srtool
- เมื่อไม่ได้เชื่อมต่อ GaC-Srtool เซิร์ฟเวอร์จับคู่ EventID ของมอนในฉากกับ PlaneEvent ของ Client แล้วใช้ StageConfig ของด่านนั้น ทั้งตอนโจมตีมอนและตอนมอนโจมตีผู้เล่น ไม่มีการใช้รายการมอน Custom จากไฟล์เก่าเป็น fallback หากไม่มีข้อมูลตรงรุ่นจะคืน error พร้อม log
resources/WorldBattleConfig.jsonเก็บ PlaneEvent/Cocoon ที่อ่านจาก Client พร้อมเวอร์ชันและ hash ของ assembly;world_levelเป็นระดับบัญชีที่ใช้งานร่วมกันใน login/ฉาก/Battle ตารางนี้ต้องส่งออกใหม่เมื่อเปลี่ยน Client หรือชุด resource- GaC-Srtool ส่ง
POST /api/tool-sessionพร้อมclient: "GaC-Srtool"และconnected: trueแล้วต่ออายุตามsrtool.lease_secondsจึงเปิดใช้ Custom monsters ได้ การปิดเว็บ/ขาดการเชื่อมต่อทำให้ lease หมดอายุ โดยเก็บไฟล์ชั่วคราวตามsrtool.lease_fileต้องใช้เว็บฉบับที่รองรับการต่ออายุนี้และ reload เว็บหลังอัปเดต - Challenge ที่กำลังเล่นใช้ config ของโหมดและห้องนั้น มีลำดับเหนือ Custom monsters
- Quick Cocoon ที่ส่งจำนวน wave เป็นศูนย์ตีความเป็นหนึ่งชุดด่านตามพฤติกรรม request ที่เก็บได้ ยังต้องตรวจผลใน Client สำหรับ Calyx แต่ละประเภทหลังปรับเส้นทางนี้
resources/PropInteractionConfig.json ระบุวัตถุทำลายได้จาก Prop ของ Client ส่วนก้อนฟื้น Technique MP ใช้นโยบายเพิ่ม 2 จาก reference server ที่ระบุในไฟล์และจำกัดไม่เกินเพดานทีม ระบบฟื้น HP/สุ่ม buff จากวัตถุยังรอข้อมูลกราฟของ Client จึงยังไม่อ้างว่ารองรับครบ
Hide UI ใช้ปุ่ม Backslash เพื่อซ่อน HUD รวม CanvasGroup ย่อยใน Battle และเก็บชื่อเซิร์ฟเวอร์/UID ไว้ หน้าต่าง Eidolon ซ่อน controls โดยไม่ปิดกล้องหรือภาพประกอบ การเปลี่ยน Lua มีผลผ่าน heartbeat; ผลใน Battle และหน้าต่าง EX ต้องตรวจจาก Client อีกครั้ง
resources/PlayerCosmetics.json ใช้รายการ PlayerOutfit และ AvatarSkin ที่อ่านจาก Client การปลดของแต่งผู้บุกเบิกส่งไอเท็มเป็นเจ้าของครั้งละหนึ่งชิ้นใน GetBag ส่วนชุดที่เลือกผ่าน SetPlayerOutfit เก็บใน persistent.player_outfits และส่งกลับเมื่อ login ใหม่ ค่า Outfit ID ที่สวมรับจาก selector ของ Client โดยตรงและ sync ตามโครงสร้าง PearlSR; ยังไม่ได้ export ตารางจับคู่ OutfitID/ItemID จึงไม่แปลงเลขด้วยสูตร
ข้อมูลฉากที่ปรับรอบนี้จำกัดที่ Starfroster กับ Moment of Joy ใน resources/SceneMetadata.json: อัปเดตกลุ่ม/prop ที่ยังมีอยู่และเป็นของ Server พร้อม SavedValues จาก RuntimeDimension.DefaultValue จึงไม่ใช้ MaxValue เป็นค่าเริ่มต้นของสองแมพนี้ กลไกรถไฟเป็น prop ฝั่ง Client และมีตัวแปร FSV_Coaster ในตารางปัจจุบัน ผลการแสดงฉากและปุ่มรถไฟยังต้องยืนยันในเกม ส่วนแมพอื่นยังใช้ข้อมูลเดิม ไม่ได้อ้างว่าอัปเดตทั้ง resource แล้ว
สองแมพนี้ยังส่ง GroupPropertyConfig ให้ GetSceneMapInfo และกลุ่มฉากของ Server โดยใช้ชื่อ/ค่าเริ่มต้น/ขอบเขตจาก Client ค่าที่เปลี่ยนเก็บใน persistent.scene_group_properties ข้อมูลที่เก็บครอบคลุม Main dimension ที่ระบุใน resource; handler จะปฏิเสธ dimension หรือ property ที่ยังไม่มีข้อมูล ข้อสันนิษฐานของ schema: field 15 ชนิด int32 ใน UpdateGroupPropertyCsReq ใช้เป็นค่าที่ต้องการตั้ง ส่วน response ยืนยันเฉพาะชื่อ/floor/group/dimension ตาม PearlSR โดยยังไม่ระบุความหมายของ field ค่าเก่า/ค่าใหม่ที่ obfuscated ต้องยืนยันผลจาก Client ต่อ
เทคนิคฟันทิ้งมอนใช้รายการ avatar จาก reference PearlSR และ Rank ของ NPCMonsterData ที่ส่งออกจาก Client มอนระดับ Minion/MinionLv2 จึงข้าม Battle ได้ ส่วนบอส/Challenge และช่วงใช้ Custom encounter คงเส้นทาง Battle ของโหมดนั้น ไม่มีการตัดสินชนิดมอนด้วยช่วงเลข ID
ทำไมมี executable สองตัว
| ไฟล์ | หน้าที่ | พอร์ต |
|---|---|---|
gameserver-go.exe |
รับการเชื่อมต่อเกมผ่าน KCP และจัดการผู้เล่น ฉาก และ battle | UDP ตาม game_port |
sdkserver-go.exe |
Dispatch, login, ข้อมูล hotfix และ REST API | HTTP ตาม sdk_port |
การเปิดระบบครบต้องรันทั้งสองตัว .exe ที่ build เสร็จจะอยู่ใน root ของโปรเจกต์ ให้เปิดจาก root เพื่อให้เซิร์ฟเวอร์หาไฟล์ข้อมูลและบันทึก log ได้ถูกตำแหน่ง
Build และรันเอง
รันคำสั่งจาก root ของโปรเจกต์:
go build -o gameserver-go.exe ./cmd/gameserver
go build -o sdkserver-go.exe ./cmd/sdkserver
เปิดคนละ terminal:
.\sdkserver-go.exe
.\gameserver-go.exe
ซอร์สมีไฟล์ protobuf ที่ generate แล้ว การ build ปกติไม่ต้องใช้ protoc หากแก้ schema จึงต้องติดตั้ง protoc และ protoc-gen-go เพื่อ generate ใหม่
สำหรับนักพัฒนาที่ต้องการรันชุดทดสอบ:
go test ./...
ผลทดสอบฝั่ง Go และการ build สำเร็จไม่ได้ยืนยันว่า Client โหลดฉากหรือเข้า battle สำเร็จ ต้องตรวจในเกมและอ่าน Player.log ประกอบด้วย
ไฟล์ข้อมูล
| ไฟล์หรือโฟลเดอร์ | ใช้สำหรับ |
|---|---|
freesr-data.json |
ตัวละคร, lightcone, relic และ battle_config |
persistent |
ทีม, ตำแหน่ง, ฉาก และค่าผู้เล่นที่บันทึกไว้ |
res.json |
ข้อมูลฉากหลักในรูปแบบที่ Go loader ใช้ |
resources/ |
ข้อมูลฉากและตารางประกอบ เช่น MapEntrance, MazePlane, Anchor และ StageConfig |
teleports.json |
ตำแหน่ง teleport แยกตาม floor |
challenge_data.json |
ข้อมูล challenge |
mission_unlocks.json |
ข้อมูลภารกิจและการปลดล็อก |
hotfix.json, versions.json |
ข้อมูลเวอร์ชันและทรัพยากร Client สำหรับ SDK server |
scripts/ |
Lua ที่ส่งให้ Client ผ่าน heartbeat |
สำรอง freesr-data.json และ persistent ก่อนเปลี่ยนชุดข้อมูล res.json ใน root กับ resources/res.json มีโครงสร้างต่างกันและ loader ใช้ร่วมกัน จึงควรเก็บไว้ตามตำแหน่งเดิม
FreeSR custom battle ใช้ได้เฉพาะ Golden/Crimson Calyx ตาม CocoonType จาก Client resource; Stagnant Shadow และ Cavern of Corrosion จะใช้ Stage/monster จาก Client ตามเวอร์ชันเสมอ ส่วน Echo of War ใช้ StartRaid ซึ่งยังไม่มี handler สร้างฉากและ BattleInfo ใน server ปัจจุบัน จึงยังต้องเติม Raid resource/handler แยก โดยห้ามอ่าน battle_config ของ FreeSR
REST API / GaC-Srtool
เมื่อ SDK server ทำงาน เปิด URL ที่ตั้งใน public_sdk_url เพื่อดูรายการ endpoint หน้า root และ /dashboard ตอบข้อมูล JSON ของ API gateway
| Method | Endpoint | หน้าที่ |
|---|---|---|
| GET | /api/status |
อ่านสถานะและข้อมูลผู้เล่น |
| GET, POST | /api/freesr |
อ่านหรืออัปเดต FreeSR data |
| GET, POST | /api/inventory/materials |
อ่าน catalog Item ID ของ Client หรือแก้จำนวนไอเทมหลายรายการโดยไม่เขียนทับ FreeSR data ส่วนอื่น |
| GET, POST | /api/freesr/avatars |
อ่านหรืออัปเดตข้อมูลตัวละคร |
| GET, POST | /api/persistent |
อ่านหรืออัปเดตข้อมูล persistent |
| GET, POST | /api/battle_report |
อ่านสถิติดาเมจ/การต่อสู้ล่าสุด หรือบันทึกรายงานการต่อสู้ |
| POST | /api/battle_report/live |
รับ snapshot สถิติ protobuf ระหว่าง Battle จาก Client ที่ใช้ Lua collector |
| POST | /api/battle_report/reset |
ล้างสถิติรายงานการต่อสู้ |
| POST | /api/exrank/toggle |
สลับการปลดล็อก EX rank |
| POST | /api/avatar/rank |
เปลี่ยน rank ตัวละคร |
| POST | /api/avatar/max_all |
ปรับข้อมูลตัวละครตามการทำงานของ handler |
| POST | /api/teleport |
อัปเดตข้อมูลจุดหมาย teleport |
| POST | /api/command |
ส่งคำสั่ง GM ไปยังเซิร์ฟเวอร์ |
| GET | /api/data-in-game |
อ่านข้อมูลเกมที่แคชไว้ในเซิร์ฟเวอร์ |
รูปแบบ request และเงื่อนไขการอัปเดตอยู่ใน internal/sdkserver/api.go การอัปเดตข้อมูลผ่าน API ยังต้องตรวจผลการโหลดข้อมูลของ Client ด้วย
Damage Analyst รับ snapshot ระหว่างสู้จาก BattleModule.LastBattleStatistic ผ่าน Lua ใน scripts/ แล้ว Srtool อ่าน snapshot ล่าสุดจาก /api/battle_report โดยอัตโนมัติ รายงานสุดท้ายยังมาจาก PVEBattleResult/QuitBattle ตามเดิม หากไม่มี snapshot ใหม่เกิน battle_diagnostics.live_report_stale_seconds จะกลับไปแสดงรายงานล่าสุดที่บันทึกไว้ ค่าเริ่มต้นอยู่ใน server.json และ override ได้ด้วย server.local.json หรือ GAC_LIVE_BATTLE_STALE_SECONDS การอัปเดตนี้ยังต้องยืนยันการทำงานจริงจาก Client
สถานะและปัญหาที่กำลังตรวจ
มี handler สำหรับ login, ทีม, ตัวละคร, ไอเทม, ฉาก, teleport, ภารกิจ, Calyx และ challenge แต่ความครบของ handler ไม่ได้หมายถึงรองรับทุกกรณีใน Client
| รายการ | สถานะ |
|---|---|
| Error 2000 ตอน login | แก้ GetCurBattleInfo ให้ตอบสำเร็จพร้อมข้อมูลว่างเมื่อไม่มี battle; รอยืนยันในเกม |
| Calyx | ผู้ใช้ยืนยันว่าเข้า Battle ได้แล้ว 17 ต.ค. หลังแก้เส้นทาง BattleInfo |
| ตีมอนแล้วค้างหน้า Weakness | ผู้ใช้ยืนยัน Stagnant Shadow ที่เรียกมอนออกมาให้ตีเข้าสู้ได้แล้ว 17 ต.ค.; log มี PVEBattleResult ส่วนมอนทั่วไปยังไม่ได้ยืนยันแยก |
| เปิดหน้าตัวละครแล้วค้าง | ผู้ใช้ยืนยันว่าหายหลังเปิดเกมใหม่; ไม่พบ PlayerBoard/Inventory error ใน log รอบใหม่ |
| วาร์ปแมพใหม่ | ผู้ใช้ยืนยัน Starfroster/Ferry Relay Station และ Moment of Joy วาร์ปได้แล้ว 17 ต.ค.; กรองกลุ่มที่ Client ระบุ IsDelete=true ด้วย SceneMetadata จากตาราง Client |
| Endgame | ตรวจข้อมูลทุกด่านที่ติดตั้ง 1,747 รายการห้อง/node บน 34 floors; เพิ่มจุดเกิดจริง, PF 4 cycles และ flow เปลี่ยนทีม/ห้อง/สรุปผลสำหรับด่านหลัก, Tierce และ Peak แล้ว รอผลใน Client ดู ผล audit และข้อจำกัด |
SceneCastSkill |
ใช้รายการ monster entity ที่ server สร้างเพื่อตรวจเป้าหมายก่อนส่ง battle |
ข้อมูลล่าสุดที่ตรวจในซอร์สและเอกสาร: 17 ตุลาคม 2026
เก็บข้อมูลเมื่อพบปัญหา
| ไฟล์ | ข้อมูลที่ช่วยวิเคราะห์ |
|---|---|
gameserver.log |
คำขอและแพ็กเก็ตตอบกลับ |
server_debug.log |
รายละเอียด battle, scene และ teleport |
battle_last.json |
Payload battle ล่าสุดที่สร้างสำหรับ Calyx หรือการตีมอน |
battle_last_response.bin, battle_last.bin |
ไบต์ response ที่ส่งจริง และ nested battle_info ที่ตัดจาก response นั้น |
battle_last_capture.json, battle_diagnostics/ |
CmdID, เวลา, SHA-256 และ snapshot ของแต่ละรอบ |
Player.log ของ Client |
Exception และจุดที่ Client โหลดข้อมูลไม่สำเร็จ |
ตำแหน่ง Player.log บน Windows สำหรับ Client ที่ใช้ในงานนี้:
%USERPROFILE%\AppData\LocalLow\Cognosphere\Star Rail\Player.log
เมื่อรายงานปัญหาให้ระบุเวอร์ชัน Client, เวลาที่เกิด, การกระทำก่อนค้าง และรหัส Cocoon/Entry/Teleport ถ้ามี พร้อม log ช่วงเดียวกันทั้ง server และ Client ข้อความ sent packet ยืนยันเพียงว่า server ส่งแพ็กเก็ตแล้ว ส่วนการโหลดสำเร็จต้องดูฝั่ง Client
สำหรับการตรวจ battle ทีละส่วน ดู บันทึกเทียบ upstream ค่า battle_diagnostics.client_battle_type ใน local config เปิด probe ที่อ่าน payload ด้วย protobuf parser ของ Client และเขียน [GAC_BATTLE_WIRE] ลง Player.log ชื่อ class ต้องอ้างอิง stack trace ของ Client รุ่นนั้น ตั้งค่าว่างเพื่อปิด probe
ประวัติรอบ 16 ต.ค. พบ main affix ผิดใน inventory ชุดเดิมและแก้ข้อมูลไปแล้ว ผลนั้นไม่ใช่หลักฐานว่า inventory ที่นำเข้าภายหลังหรือการเข้า Battle ปัจจุบันผ่าน ดูผลแต่ละรอบในบันทึกตรวจสอบ
การแก้ Battle รอบ 17 ตุลาคม 2026
SceneCastSkillอ่านทั้ง hit targets, assist IDs และ assist groups และตอบMonsterBattleType=TRIGGER_BATTLEตาม enum ของต้นฉบับ; ค่าเริ่มต้นศูนย์หมายถึงNONE- โหลดธาตุตัวละครจาก
resources/AvatarConfig.jsonเพิ่มเติม เพราะavatarConfigsในres.jsonอาจว่าง - กฎบัฟเฉพาะตัวละคร, global passive, entry attack และ invasion อยู่ใน
resources/BattleRules.jsonพร้อม upstream revision ต้องนำไฟล์นี้ไปด้วยเมื่อแจก binary; ไม่มีการฝัง ID ของกฎเหล่านี้เป็น fallback ในตัว executable - Battle SP แปลงจากอัตราส่วนในข้อมูลที่นำเข้าเป็นสเกล 10000 และ HP มอนที่เป็นศูนย์คงศูนย์ตามรูปแบบ upstream
- Invasion ใช้สถานะ challenge ที่โหลดจริง
battle_config.enable_invasion_supportเปิดบัฟ support เพิ่มได้ (ค่าเริ่มต้น false) - เลิกสร้าง Material ID ด้วยสูตร
130000 + AvatarIDซึ่ง Client หาไม่พบ และไม่ตอบข้อมูลว่างให้โมดูล RogueTourn/ChenLing ที่ยังไม่รองรับ
สถานะรอบนี้: build ผ่าน วาง binary ที่ root แล้ว ผู้ใช้ยืนยันทั้ง Calyx และ Stagnant Shadow เข้า Battle ได้ โดย log ของเส้นทางตีมอนเวลา 11:53:08 มี BattleInfo และเวลา 11:53:16 มี PVEBattleResult ผลนี้ยังไม่ใช่การรับรองโหมดต่อสู้ทุกประเภท
แก้โปรไฟล์เพิ่มเติม: field 4 ของ GetPlayerBoardDataScRsp คือการ์ดส่วนตัว, field 8 คือรูปโปรไฟล์ และ field 9 คือตัวละครช่วยเหลือ โค้ดเดิมใส่ ID ผิดประเภทและไม่มี head frame/display data ปรับให้ใช้ resources/AvatarPlayerIcon.json, resources/ProfileConfig.json และทีมจริง พร้อมเติม ArchiveData ทั้งสอง response ผู้ใช้ยืนยันหน้าตัวละครหายค้างหลังเปิดเกมใหม่
การสลับ Path ของ Trailblazer อ่าน path/เพศจาก persistent ส่งข้อมูล avatar และทีมตามลำดับ protocol ที่อ้างอิง พร้อมบันทึกค่าที่เปลี่ยน โปรไฟล์เก็บค่ารูป, การ์ด, signature, ตัวละครแสดงผล/ช่วยเหลือ และมือถือ (theme, case, chat bubble) ไว้ใต้ persistent.profile และตรวจ ID กับ resource ของ Client ก่อนรับค่า จึงยังคงค่าที่เลือกหลังเปิดเกมใหม่
Eidolon และ EX: การแจกไอเท็มและซิงก์
resources/EidolonConfig.json กำหนดรายการแจกของ Jing Yuan, Dan Heng IL และ Acheron:
| ตัวละคร | ก้อน E1–E6 | ก้อน EX |
|---|---|---|
| Jing Yuan | 11204 | 131204 |
| Dan Heng IL | 11213 | 131213 |
| Acheron | 11308 | 131308 |
ก้อนปกติแจกเท่ากับต้นทุน rank ที่ยังไม่ปลด (E0 ได้ 6 ชิ้น) อ้างอิง AvatarRankConfig.UnlockCost และ ItemConfigAvatarRank จาก ชุดข้อมูล revision ที่บันทึกไว้ และเทียบ RankIDList กับ AvatarConfig ในโปรเจกต์ ก้อน EX ใช้สาม ID ที่ผู้ใช้ระบุ แจกหนึ่งชิ้นเมื่อ EX ปิดอยู่ ไม่สร้าง ID ด้วยสูตรจาก AvatarID ผู้ใช้ยืนยันรายการใหม่และการกดปลดใน Client แล้ว
GetBagส่งรายการแจกเมื่อเข้าเกม ก้อนลดตาม rank/EX ที่บันทึกไว้ ไม่รีเซ็ต progression เมื่อเปิดเกมใหม่- เมื่อนำเข้า FreeSR ระหว่างออนไลน์ การซิงก์เต็มส่งจำนวนวัสดุแบบเดียวกับ
GetBagด้วย การแก้เฉพาะตัวละครหรือวัสดุส่งเฉพาะจำนวนที่เปลี่ยน รวมค่า 0 เมื่อปลดครบ โดยยังเลื่อนการนำเข้าจนจบ Battle ตามเดิม - API นำเข้า
PUT /api/freesr/dataเก็บmaterialsเดิมไว้เมื่อไฟล์นำเข้าไม่ได้ส่งฟิลด์นี้มา ถ้าส่งmaterialsมาเองจะใช้ค่าที่ส่ง รวมจำนวน 0; ส่ง{}เพื่อล้าง override กลับไปใช้จำนวนจาก resource/progression กฎนี้ไม่ครอบคลุมโปรแกรมภายนอกที่เขียนทับไฟล์โดยตรง - ตามคำขอเก็บก้อนแม้ปลดครบแล้ว ให้ตั้งจำนวนแยกใน
materialsผ่าน/api/inventory/materialsโดย API รวม ID จากEidolonConfig.jsonด้วย จำนวนที่ตั้งไว้จะใช้แทนการแจกตาม rank/EX และไม่เปลี่ยน E6 หรือสถานะ EX ของตัวละคร GetBagโหลด catalog จากresources/InventoryItemConfig.json: Material/Usable/Mission 2,612 รายการ, Light Cone 170 ใบ และ Relic BASIC ที่ Client มองเห็นตาม ItemConfigRelic โดย join กับ RelicConfig และกลุ่ม affix ของแต่ละชิ้น ของ catalog ที่ไม่มีใน save จะเพิ่มเป็นชิ้นยังไม่สวมและบันทึกลงfreesr-data.json; อุปกรณ์เดิมไม่ถูกลบ- RankUpAvatar เซฟ rank ลง freesr-data.json โดยแก้เฉพาะค่า rank และเก็บฟิลด์นำเข้าอื่นไว้ E1–E6 ไม่เปิด EX ให้เอง
- EX ทั้งสามคนเริ่มปิดไว้ตามคำขอ
falseเก็บชัดเจนใน persistent; การเปิดจากในเกมบันทึกและส่ง AvatarSync/MaterialList เฉพาะส่วน - เกมอ่าน EX จาก persistent ทุกประมาณ 1 วินาที แล้วส่งข้อมูลตัวละครที่เปลี่ยน ใช้เส้นทางนี้ร่วมกับ API เดิมโดยไม่แก้ source เว็บ GaC srtool
- ระหว่าง Battle เลื่อนการนำเข้า EX และ freesr-data ไปหลัง PVEBattleResult เพื่อไม่เปลี่ยนอุปกรณ์หรือข้อมูล actor กลางการต่อสู้
- โหลดข้อมูลไม่เซฟกลับอัตโนมัติอีกแล้ว SavePersistent รวมเฉพาะค่าที่เปลี่ยนจาก snapshot ของผู้เขียน รักษาฟิลด์อื่นที่ API แก้ พร้อม lock ข้าม process และเขียนไฟล์แบบ atomic ต้องใช้ SDK/Game server build ใหม่ทั้งคู่
สถานะรอบ progression: build ทั้งสองตัวผ่าน ผู้ใช้ยืนยันว่าก้อนขึ้น ปลด E1/EX ได้ และยังเข้า Calyx ได้ ตรวจไฟล์ระหว่างลองพบ Dan Heng IL rank=1 และ EX=true ต่อมาผู้ใช้เปิด EX ทั้งสามคนแล้ว จึงเก็บค่า true ตามการกดปลดจริง ผลนี้ไม่ได้รับรองว่าเว็บ GaC srtool ที่แก้แยกอยู่ได้ทดสอบ integration แล้ว
รอบโหลดกระเป๋าเดิม 12:18 ก่อนเพิ่ม item catalog ส่ง GetBagScRsp ขนาด 56663 ไบต์ ไม่พบ TryCreateItemData/Inventory error ใน log ณ เวลาตรวจ หลังจากนั้นรับ RankUpAvatar เวลา 12:18:23 และคำขอ EX เวลา 12:18:28 สำเร็จ พร้อม PlayerSyncScNotify ขนาด 234/237 ไบต์; ขนาดและผล Client ของ catalog ใหม่ยังต้องตรวจในเกม
โครงสร้างซอร์ส
cmd/gameserver/ จุดเริ่ม game server
cmd/sdkserver/ จุดเริ่ม SDK/dispatch server
internal/common/ โมเดลข้อมูล, resource loader และ protobuf converters
internal/gameserver/ Gateway, session, dispatch และ game handlers
internal/sdkserver/ HTTP dispatch, login และ REST API
protocol/proto/ Schema, generated protobuf และตาราง CmdID
pkg/kcp/ KCP transport
resources/ ข้อมูลประกอบ
scripts/ Client Lua
อัปเดต protocol และ handler
ใช้ checkout ของ PearlSR ที่ตรวจ revision แล้วเป็นแหล่งข้อมูล ไม่แก้ generated Go ด้วยมือ:
python tools/sync_protocol_commands.py "<path-to-pearl-sr-checkout>"
protoc --go_out=. --go_opt=paths=source_relative protocol/proto/StarRail.proto protocol/proto/current_handlers.proto
python tools/audit_protocol_handlers.py "<path-to-pearl-sr-checkout>"
คำสั่ง sync อัปเดตตารางชื่อ/CmdID ส่วนการแก้ field ต้องตรวจจาก schema ต้นฉบับแล้วแก้ .proto ก่อน generate ใหม่ ตัว audit ตรวจ CmdID, คู่ request/response และชุดหมายเลข field ของ message ที่มีชื่อร่วมกัน ไม่ตรวจชนิด field, default values, asset หรือผลใน Client
ClientDownloadDataLuaVersion = 51 เป็นค่า version ของ payload ที่ upstream ปัจจุบันยังใช้ส่ง Lua จึงไม่เปลี่ยนเป็น 53 ตาม client_version โดยไม่มีหลักฐานรองรับ ส่วน Tutorial อ่าน ID จาก resources/TutorialData.json และ resources/TutorialGuideGroup.json หลังเปลี่ยน resource ให้รีสตาร์ต server
ที่มา
พอร์ตจาก GaC-hkrpg-ps ภาษา Rust โดยใช้แนวทางและข้อมูลประกอบจาก HonkaiSlopRail/pearl-sr ภาษา Zig การแก้ใน Go ต้องตรวจเทียบทั้ง handler, payload และเวอร์ชัน Client ที่ใช้งานจริง
Traces: หน้าตัวละครและ GaC-Srtool
AvatarPathData.avatar_path_skill_tree.point_idสำหรับหน้า Traces ใช้เลขAnchorTypeจากAvatarSkillTreeConfig.jsonส่วนBattleAvatar.skilltree_list.point_idใช้PointIDเต็ม อ้างอิง PearlSR commit005d2c12db4781071f4b5b68e8f364587886a710ในavatar_mgr.createSkillTreeและbattle_mgr.createBattleAvatar- แปลง
data.skillsในเซฟเป็นเลข Anchor เฉพาะตอนส่งข้อมูลหน้าตัวละคร ไม่เปลี่ยน ID ในเซฟหรือ Battle และไม่บังคับอัป Traces ให้เต็ม - GaC-Srtool บันทึก Traces ผ่านการส่ง
freesr-dataทั้งชุด ไม่ใช่/api/freesr/avatarsซึ่งรับเฉพาะข้อมูลทั่วไปของตัวละคร โค้ดเว็บที่แก้ในโปรเจกต์ GaC-srtool เพิ่มพารามิเตอร์closeModal = trueให้saveAvatarDetailsเพื่อแก้ ReferenceError ก่อนถึงขั้นส่งเซฟ ต้องใช้เว็บฉบับที่มีการแก้นี้ด้วย - การแก้เซฟจากเว็บถูกนำเข้าเกมโดย watcher; ระหว่าง Battle จะรอจนจบก่อนส่ง sync การยืนยันผลหน้า Traces และการบันทึกจากเว็บยังต้องตรวจใน Client
Technique points และอาหาร
resources/TechniqueRules.jsonเก็บค่า MP, cap ของทีม, ค่าใช้ Technique ตามตารางMazeSkillของ Client และอาหารที่ฟื้น MP โดยไม่มีผลลด HP มี revision ต้นทางและ hash ของ GameAssembly ที่อ่านข้อมูล- Phainon / Himeko Nova ใช้ cap 7 ตาม
lineup_mgr.buildLineupของ PearlSR revision ที่ระบุใน resource ตัวโหลดตรวจสมาชิกจาก ค่า AvatarID ในช่องทีม ไม่ใช่ key ของช่อง ไม่มีการบวก cap ทั้งสองตัวทับกันตาม upstream - แจกอาหารใน resource ชนิดละ
initial_food_countหนึ่งครั้งเมื่อเปิดกระเป๋า บันทึกจำนวนคงเหลือในpersistent.technique_food_stockกินด้วยUseItemหรือกินอัตโนมัติเมื่อแต้มไม่พอใช้ Technique; ห้ามกินระหว่าง Battle persistent.auto_technique_foodเปิดโหมด auto; ค่าเริ่มต้นมาจากauto_food_defaultใน resource ปิด/เปิดได้ผ่านPOST /api/techniqueด้วย{"auto_food":false}/{"auto_food":true}และอ่านสถานะด้วยGET /api/techniqueใช้ SDK URL จาก config ตัวเฝ้าดูนำสวิตช์ใหม่เข้า session หลัง Battle โดยไม่ล้าง inventory- MP ที่ใช้แล้วเก็บใน session; เริ่มห้อง Challenge ใหม่คืนค่าเริ่มต้นตาม resource การเปลี่ยนตัวละคร/วาร์ปไม่เติม MP เต็มโดยอัตโนมัติ
- Technique ที่ไม่เปิด Battle อ้างอิง
IgnoreBattleและgetBattleTypeของ PearlSR ตอบผลNO_BATTLEให้มอนที่โดน ไม่บังคับเข้า Battle จากการแปะ debuff
สถานะ: ผู้ใช้ยืนยัน PF ปกติเข้าสู้และเปลี่ยนห้องได้ ก่อนรอบแก้ Technique/อาหาร ส่วนรอบนี้ยังต้องยืนยัน Traces, แต้ม, อาหาร/auto และ Starward ห้อง 3 จาก Client ห้ามถือว่า build ผ่านเป็นผลยืนยันการเล่น
Starward: ทีมและคะแนนที่เซฟไว้
- ผู้ใช้ยืนยันว่า PF Starward เล่นจบครบ 3 ห้องได้แล้ว
- เก็บทีมและบัฟรายห้องใน
persistent.tierce_progressตอนตั้งทีม/เริ่มเล่น อ่านกลับใน GetChallengeTierceData และเริ่มห้องที่เลือกด้วยทีมของห้องนั้น ไม่ใช้ทีมแรกแทนเมื่อเล่นแยกห้อง - บันทึกคะแนนจริงจาก PVEBattleResult แยกแต่ละ node พร้อมผลที่ดีที่สุด การลองห้องเดียวใช้ผลที่เซฟของห้องอื่นประกอบ; การเล่นครบชุดเริ่มคะแนนของรอบใหม่จากศูนย์
resources/ChallengeTargetConfig.jsonเป็นข้อมูลเกณฑ์จาก Client: TOTAL_SCORE ใช้ตัดสินดาวสำหรับ PF/AS Starward พร้อมส่ง FinishedTargetList ทั้งหน้าสรุปและข้อมูลที่อ่านกลับ คะแนน 97,000 ได้เกณฑ์ PF 60,000 / 75,000 / 90,000 แต่ยังไม่ถึงเกณฑ์พิเศษ 99,000- รอบนี้ยังไม่เพิ่มการแจก reward, การจัดอันดับออนไลน์ หรือการประเมิน target ประเภท ROUNDS_LEFT/DEAD_AVATAR ของ MoC ส่วนข้อมูลประวัติ Endgame ปกติ/Peak เดิมยังมีรายการสำเร็จรูปตามข้อจำกัดในเอกสาร audit
- ต้องตรวจใน Client ว่ากลับมาแล้วทีมยังอยู่ เลือกเล่นห้องเดียวได้ และหน้าสรุปดาวตรงกับคะแนน Build ไม่ใช่การยืนยันผลเล่น
วาร์ป MoC จากสมุดผ่านฉากรถไฟ
แก้ GetPamSkinData ที่เคยส่งสกินปอมปอม ID0 ซึ่งไม่มีในตาราง Client และทำให้ NPC โหลดล้ม อ่าน default/รายการที่ปลดจาก ProfileConfig และตรวจ ConfigEntityPath กับ PamSkinConfig.json แทน ค่าต้นทางมี revision/assembly hash ตรวจสอบได้ ยังรอผู้ใช้ยืนยันการวาร์ปรอบแก้สกินนี้
Preset ทีมในเกม
- เปิดไว้ 20 ช่องผ่าน
resources/TeamPresetConfig.json(preset_countเป็นนโยบายจำนวนทีมที่ผู้ใช้ขอเพิ่ม; ขอบเขตสมาชิก 4 และชื่อ 14 ตัวอักษรอ้างอิง Client) - GetAllLineupData ส่งรายการครบพร้อม index ปัจจุบัน รองรับสลับทีม ตั้งชื่อ เพิ่ม/ถอด/สลับสมาชิก และ MarkPresetLineup สำหรับทีมโปรด/Quick Lineup
- บันทึกใน
persistent.team_presetsและactive_team_presetเก็บlineupsเดิมเป็นสมาชิกทีมที่กำลังใช้อยู่เพื่อใช้กับ API เดิม ไม่เขียนไฟล์ตอนอ่านเพื่อสร้าง preset เปล่า - เริ่มใช้ด้วยทีมเดิมในช่องแรก ช่องอื่นยังว่างจนจัดสมาชิกครั้งแรก จากนั้นเลือกทีมที่เซฟไว้ได้โดยไม่ต้องจัดใหม่ทุกครั้ง ปฏิเสธการสลับไปทีมว่างและการแก้ทีมระหว่าง Battle
- เมื่อมีการเซฟ preset การสู้ทั่วไป/Calyx ใช้สมาชิกทีมที่เลือก แต่คง stage/waves จากข้อมูลเดิม ห้อง Endgame ใช้ทีมประจำห้องของ ChallengeRun
- Build ผ่านยังไม่ยืนยันว่า UI ของ Client แสดงครบ 20 ช่องและใช้งาน Quick Lineup ได้ทุกหน้า ต้องตรวจเพิ่มในเกม การจัดทีม Endgame โดย request ExtraLineupType ยังคงเป็นคนละเส้นทางกับการแก้ preset ทีมเดินโลก
Freecam
Tab: เปิด/ปิด Freecam; ลูกศร: เลื่อนกล้อง; กดเมาส์ขวาค้างแล้วขยับเมาส์เพื่อหันกล้องPage Up/Page Down: ขึ้น/ลง;+/-: เพิ่ม/ลดความเร็ว ไม่ใช้ E/Q หรือ I/J/K/L คุมกล้อง- ขณะเปิด Freecam ใน Battle ซ่อนการรับคลิก UI ด้วย
InControlInputModule.allowMouseInputและพักเฉพาะ mouse binding ซ้าย/ขวาของคำสั่งเกมในหน่วยความจำ คีย์บอร์ดยังกดสกิลได้ ส่วนกล้องอ่านเมาส์ผ่าน Unity Input โดยตรง - คืนค่าตอนปิด Freecam/ออก Battle หลังปล่อยปุ่มเมาส์ และก่อนปิด Client ไม่มีการลบ/เพิ่ม binding ผ่าน OnBindingsChanged ชื่อ field ตรวจจาก Client โดยตรง ผลป้องกันคลิกใน Battle ยังรอยืนยันการเล่นจริง
- สคริปต์อัปเดตผ่าน heartbeat โดยไม่ต้อง build หรือรีสตาร์ตเซิร์ฟเวอร์
Hide UI
Backslash ซ่อนเฉพาะ HUD ที่ระบุชื่อไว้ใน scripts/hide_ui.lua เช่น HUD เดินโลก/ต่อสู้ ตัวเลขลอยและ waypoint โดยไม่ปิด UIRoot, กล้อง UI หรือ Canvas ทั้งฉาก จึงเหลือหน้าตัวละคร หน้ารายละเอียด EX Eidolon และหน้าต่างรางวัลไว้ ปิดโหมดแล้วคืนค่า CanvasGroup เดิมของ HUD ที่แก้ ถ้าเคยใช้สคริปต์ซ่อนทั้ง UI รุ่นเก่า แนะนำเปิดเกมใหม่หนึ่งครั้ง เพราะรุ่นเก่าไม่ได้เก็บค่า alpha/interactable เดิมทั้งหมดไว้ ผลหน้า EX ยังรอยืนยันจาก Client
Hide UI ซ่อนเมนู รายละเอียด รายชื่อตัวละคร และปุ่มปิด/แถบบนที่ตรวจพบในหน้า Character เพิ่มด้วย ภายในหน้า Rank/Eidolon ซ่อนข้อความ ปุ่ม และรูปไอคอน SkillIcon โดยคงภาพประกอบไว้ รวมถึงเว้นชื่อเซิร์ฟเวอร์และ UID การตรวจชื่อองค์ประกอบนี้ยังต้องยืนยันทั้งหน้า EX และ Battle ใน Client
การตรวจรอบล่าสุดยืนยันจากผู้ใช้ว่า prop ของ Moment of Joy โหลดได้แล้ว ส่วน Starfroster พบว่า RuntimeFloor มี 175 instance แต่มี GroupID ไม่ซ้ำ 167 กลุ่ม จึงแก้ group_count ใน SceneMetadata ให้นับ ID ไม่ซ้ำ มิฉะนั้นตัวตรวจจะไม่ใช้ข้อมูลใหม่และ Client ยังได้รับกลุ่มที่ถูกลบ รายการ checkpoint ที่ส่งออกของสองแมพใช้สถานะ CheckPointEnable ตามคำขอปลดล็อก โดยอ้างอิง enum จาก Client ผลวาร์ป/ปุ่ม M หลังแก้รอบนี้ยังรอยืนยัน
Town / Story ในแผนที่ M
MapEntrance.jsonมี Mission2057101และ Town2057102ซึ่งใช้ Plane/Floor เดียวกัน รายการ Starfroster เดิมใช้ Town และเพิ่มStarfroster — Storyไว้ถัดลงมา โดยกำหนด Floor, Entry และชื่อที่resources/NavigationProfiles.jsonแล้วรีสตาร์ต Game server/Client เมื่อเปลี่ยนรายการscripts/navigation_profiles.luaขยายรายการจริงในSwitchAreaPageผ่านSingleTabData/SubTabDataที่ตรวจจาก Client และคัดลอกแท็บในหน่วยความจำ ชื่อ Story แยกจาก localized title เดิม เครื่องหมายตำแหน่งผู้เล่นตรวจ Entry ที่ Server ส่งด้วย จึงไม่แสดงทั้งคู่เพียงเพราะอยู่ Floor เดียวกัน ไม่สร้าง Plane/Floor/Entry ใหม่- Client เลือกแท็บซ้ำโดยใช้ FloorID แม้สองแถวมี Entry ต่างกัน สคริปต์จึงคง SubTabData ของตัวเลือกหลัง callback เลือกแถว พร้อมแก้ทั้งข้อความหัวข้อและข้อความแถวที่ LoopListView นำกลับมาใช้ ตรวจเฉพาะหน้าที่เปิดอยู่เป็นช่วง ๆ และ cache reflection ไม่สแกน assembly
- ตัวโหลดตรวจเวอร์ชัน resource และชนิด Town/Mission ให้ตรงตาราง MapEntrance รวมถึง Plane/Floor และ scene config ก่อนส่งข้อมูลใน heartbeat คำขอที่ระบุ Entry ตรง ๆ จะคง Entry นั้น เพื่อให้ Story ไม่ถูกเปลี่ยนกลับเป็น Town; คำขอที่ระบุเพียง Floor/teleport ใช้ค่าเริ่มต้นจาก
map_teleport_entry_idใน SceneMetadata - ผู้ใช้ยืนยันว่าชื่อ Story เปลี่ยนแล้ว และ log หลังแก้ callback ยืนยัน request/response ของ Story เป็น Entry
2057101และ Town เป็น2057102โดย Server ไม่เปลี่ยน Entry ระหว่างทาง ทั้งคู่ยังใช้ Floor20571001ตามตาราง Client จึงยังไม่ถือว่าแยกสภาพฉาก/แสง/สถานะเนื้อเรื่องได้ ประเภท Mission/Town ของทางเข้าเพียงอย่างเดียวไม่ยืนยันว่าภาพฉากจะต่างกัน การคืนตำแหน่งจาก Starfroster ไปจุดเดิมช่วยแก้กรณี login หน้าดำได้ตามผู้ใช้ยืนยัน แต่ไม่ถือว่าแก้ฉาก Starfroster ครบแล้ว - เพิ่มการทดลองสถานะฉากแยก Entry ใน
SceneMetadata.entry_saved_values: Town ใช้ MaxValue จาก RuntimeDimension ตามนโยบาย freeplay ของ PearlSR revision ที่ระบุ ส่วน Story ใช้ DefaultValue ตัวโหลดตรวจชื่อ/ขอบเขตจากsaved_value_rulesและคัดลอกค่าแต่ละ Entry โดยไม่เขียนทับ defaults ของ Floor ร่วมกัน GetSceneMapInfo ใช้ Entry ที่ผู้เล่นอยู่เมื่อขอ Floor ปัจจุบัน จึงไม่ส่งสถานะ Town ทับ Story เพียงเพราะใช้ Floor เดียวกัน ผลแสงและกลไกหลังแยกสถานะรอบนี้ยังรอตรวจจาก Client และไม่อ้างว่า Story เป็น snapshot เควสต์อย่างเป็นทางการ - หลังเปลี่ยนชุดสถานะ ผู้ใช้ยืนยันว่า Town กลับมาปกติแล้ว และ log ยืนยัน Entry
2057102พร้อมชุดค่าฉาก Town การเล่นเนื้อเรื่องเต็มและกลไกของ Story ยังไม่ได้ยืนยัน
Inventory ใน Client
GetBag ใช้ resources/InventoryItemConfig.json ซึ่งสร้างจาก ItemConfig/EquipmentConfig/RelicConfig revision ที่ระบุในไฟล์ เพื่อแสดงรายการ Material, Usable, Mission, Light Cone และ Relic BASIC ที่ Client มองเห็น อุปกรณ์ที่เพิ่มจาก catalog จะถูกเก็บเป็นรายการที่ยังไม่สวมใน freesr-data.json; main affix และ sub-affix ของ Relic เลือกจากกลุ่มที่ระบุใน RelicConfig ของชิ้นนั้น ID Relic ใน save ที่ไม่มี config ฝั่ง Client จะไม่ถูกส่งใน Bag, AvatarSync หรือ Battle แต่ยังเก็บไว้ใน save และมี log จำนวนรายการที่ถูกข้าม
หากแก้ catalog ให้ใช้ตารางทั้งสี่ไฟล์จาก revision เดียวกันตาม source ใน JSON แล้วรัน python tools/generate_inventory_item_config.py --source-dir "<directory-containing-the-ExcelOutput-files>" --inventory-config resources/InventoryItemConfig.json เพื่อสร้าง Relic templates ใหม่
GaC-Srtool แก้จำนวน Material/Usable/Mission ผ่าน POST /api/inventory/materials; จำนวนที่บันทึกใช้กับเกมหลังสร้าง session ใหม่ เพราะ game server เก็บ snapshot ผู้เล่นแยกจาก SDK API process.
ห้องรถไฟค้างโหลด
Floor 10000003 ใช้ RuntimeGroup/prop และ SavedValues defaults ที่ capture จาก Client revision ใน SceneMetadata.json แทนชุด MaxValue เดิมซึ่งเปิด Utility_IsDebug และกลุ่ม DesignerBackup ที่ Client ไม่มี graph asset กลุ่มที่ส่งให้ Client กรองด้วย GroupIndexList ของ Dimension ที่ capture มา เพื่อไม่ส่งกลุ่มจาก Dimension อื่นปนกัน การเข้าแมปโดยไม่ระบุ Teleport ใช้ StartAnchorID ของ Entry ก่อน checkpoint แรกของ Floor เพื่อให้ลงตรงห้อง ส่วน Entry ที่ไม่มี StartAnchorID ใช้ floor_default_anchor ที่ capture จาก Client โดยรักษา Anchor แยกตามทางเข้า รายละเอียดและขอบเขตการยืนยันอยู่ใน docs/scene-challenge-investigation.md ผู้ใช้ยืนยันว่า Party Car ลงถูกห้องและวาร์ป/เข้าหน้า AA ได้แล้ว ส่วนการเล่นต่อสู้และ settlement ของ AA ยังไม่ได้ทดสอบ
Endgame — ทีมแนะนำ
Damage Analyst ใช้ StatisticsComponent ของ Battle ปัจจุบันสำหรับข้อมูลสด พร้อมรายละเอียดรายตัวและช่องทีมจริง เก็บรายละเอียด native ตอนจบโดยตรวจให้ตรงกับผลที่ server ได้รับก่อนบันทึก ดู docs/damage-analyst-live.md สำหรับหลักฐาน Client และขอบเขตการตรวจ
AA cycle: แยกเวลาจำกัดของ Peak ออกจากตาราง MoC แล้ว โดยใช้ค่าที่ capture จาก Client ใน resources/PeakBattleConfig.json ทั้ง Start และ Restart; เก็บแพ็กเก็ตผลเต็มเพื่อจับคู่ตัวนับ AA เพิ่มเติม การแก้เลข cycle/ดาวในหน้าผลยังรอตรวจ Client รอบใหม่ ดู docs/peak-cycle-investigation.md
GetFriendRecommendLineup ส่งทีม Prydwen สำหรับ MoC floor 12 ลงใน response ของหน้าแนะนำทีม โดย resolve AvatarID จาก resources/AvatarConfig.json ของ Client ปัจจุบัน ข้อมูล snapshot/แหล่งที่มาและข้อจำกัดของ enum ที่ยัง obfuscate อยู่ระบุใน docs/prydwen-endgame-recommendations.md อัปเดต snapshot ได้ด้วย python tools/update_prydwen_endgame_teams.py --floor 12 --stage-count 3; ต้องตรวจหน้าใน Client จริงก่อนยืนยันผล
CW Preparation และคำสั่งจัดการรอบ (2026-10-20)
Client ยืนยันแล้วว่าเริ่มรอบ เปิดร้าน และลากตัวเริ่มลงสนามได้ เพิ่ม handler ซื้อ/รีเฟรช/ล็อกร้านและห้ามลง avatar ซ้ำในสนามแล้ว รุ่นแก้ชุดหลังยังรอตรวจจาก Client การซื้อเก็บตัวเพิ่มใน bench; การรวมดาวอัตโนมัติและเอฟเฟกต์เฉพาะตัวละครยังไม่ครบ
พิมพ์ใน GM Console ของ GaC srtool เมื่อเชื่อมต่อเซิร์ฟเวอร์:
/cw statusอ่านรอบ เงิน HP และเลเวลที่เซฟอยู่/cw gold <จำนวนรวม>ตั้งเงิน CW เช่น/cw gold 1000/cw hp <จำนวนรวม>ตั้ง HP ภายในค่าสูงสุดของรอบ
คำสั่งเปลี่ยนค่าถูกคิวไว้ตาม UID/รอบ และนำเข้าใน heartbeat ตอนอยู่หน้า Preparation ไม่แก้เงิน/กระเป๋า Adventure และไม่แทรกตอน Battle ข้อความ queued ยังไม่แปลว่าเกมใช้ค่าแล้ว; ตรวจ log CW_GM applied หรือ /cw status อีกครั้ง
ผู้ใช้ยืนยันว่าเข้าต่อสู้ห้องแรก Standard ได้แล้ว และ log ยืนยันการบันทึกผลจบรอบ ไปห้อง 1-2 เลือก Investment และกลับหน้า Preparation ได้ ทีมและเงินมาจากรอบที่เซฟอยู่ ไม่แทนด้วยทีม reference
ผู้ใช้ยืนยันการขายตัวที่ไม่มีอุปกรณ์ติดอยู่และซื้อ EXP แล้ว การซื้อ EXP บันทึกเงิน/ระดับ/จำนวนช่อง/อัตราร้านค้าพร้อมกัน ใช้ราคาจากรอบและจำนวน EXP จาก resource; ระดับสูงสุดไม่หักเงินเพิ่ม
ห้อง 1-2 ใน Client ปัจจุบันมี Plot Armor/เลือก Protagonist ผู้ใช้ยืนยันว่าไม่ใช่การกด Battle ธรรมดา ทางจำลอง Battle ห้อง 1-2 ที่ทำไว้ก่อนหน้านี้จึงยังไม่ใช่เส้นทางที่ยืนยันว่าเล่นได้ ต้องต่อ Spotlight selection และ effects ก่อน ส่วน Supply, ห้องถัดไป, Overclock, trait/equipment effects และ progression ตลอดรอบยังไม่ครบ
เพิ่มการรวมตัวชื่อเดียวกันและดาวเท่ากันครบ 3 ตัว โดยใช้แถวดาวของ Client ที่ตรวจ identity แล้ว สร้าง UID ใหม่และเก็บตำแหน่ง/อุปกรณ์ของตัวที่คงไว้ ตัวครบชุดที่ค้างจาก build เก่าจะรวมเมื่อกด Continue; การอ่านเซฟอย่างเดียวไม่แก้ไฟล์ ระบบพื้นฐานนี้ยังไม่ครอบคลุมตัวสลับร่างและข้อยกเว้น Spotlight/trait ที่เปลี่ยนกฎรวมดาว
เพิ่มการเซฟ/ซิงก์ติดตามอุปกรณ์และลำดับติดตามจากหน้า Preparation แล้ว ใช้ข้อมูลอุปกรณ์ตรงรุ่น ทั้งการรวมดาวและการติดตามอุปกรณ์ยังรอผลยืนยันจาก Client หลังเปลี่ยน build