23 KiB
name, description
| name | description |
|---|---|
| hkrpg-ps-version-update | คู่มือ Honkai Star Rail Private Server (GaC-hkrpg-go ภาษา Go + GaC-hkrpg-ps Rust ตัวเก่า) บทเรียนอัปเดตข้ามเวอร์ชัน client และกับดักที่พิสูจน์แล้ว ใช้เมื่อ client อัปเดตแล้วเซิร์ฟเวอร์มีอาการ จอดำ/ค้าง/กล้องหลุดตอนกดตี/packet Unknown/dialogue ค้าง/crash |
คู่มือ HSR Private Server — บทเรียนที่พิสูจน์แล้ว (อัปเดตล่าสุด: 2026-10-07)
สถานะปัจจุบัน: เซิร์ฟเวอร์หลักคือ
C:\Users\Phitchayut\Desktop\GaC-hkrpg-go(ภาษา Go, client 4.6.51) โปรเจกต์ RustGaC-hkrpg-psเป็นตัวต้นทาง/reference — โค้ด Rust ล่าสุดแตกตัวกับ proto ใน repo (ดูหัวข้อ 10) เอกสารฉบับเต็มของตัว Go อยู่ที่GaC-hkrpg-go/README.md
1. ลำดับการวินิจฉัยเมื่อ client ใหม่เข้าไม่ได้
อ่าน log ตามลำดับนี้ ก่อนแก้โค้ด เพราะอาการเดียวกันมีหลายต้นเหตุ:
| อาการ | แหล่งข้อมูล | สิ่งที่มองหา |
|---|---|---|
| ค้างก่อนหน้า login | client hook log | URL ที่ถูก redirect/block |
| จอดำหลังกด login | Player.log (LocalLow/Cognosphere/Star Rail) |
NullReferenceException + ชื่อ handler _CmdXxxScRsp + offset (offset ต่างกัน = field คนละตัวใน handler เดียวกัน) |
| จอดำ/ค้างหลัง login finish | Player.log + log หน้าต่าง Game Server |
packet ที่ client ส่งต่อแต่เซิร์ฟ "Unknown command ID" |
| ค้างหน้าโหลดฉาก | Player.log |
CreateMemberEntityLeader FAILED, LuaException (GotoMapParam ฯลฯ), ExcelTable FAILED |
| กล้องหลุดจากตัว/ค้างหลังกดตี | Player.log + server_debug.log (บรรทัด SCENE_CAST_SKILL) |
battle transition จาก target ที่ไม่ใช่ monster (ดูหัวข้อ 11) |
| เกมดับ | Temp/Cognosphere/Star Rail/Crashes |
crash report |
| จุดวาป/คุยผิดปกติ | server_debug.log ในโฟลเดอร์เซิร์ฟ |
บรรทัด ENTER_SCENE / INTERACT_PROP (บันทึก entry_id/teleport_id/prop จริง) |
กฎสำคัญ: Player.log ถูกเขียนทับทุกครั้งที่เปิดเกม — ต้องอ่านทันทีหลังทดสอบ และอย่าสรุปจาก error ของ session เก่าที่จำ offset ไว้
2. Checklist สิ่งที่ต้องอัปเดตต่อเวอร์ชัน client
- Proto/cmd dump ใหม่ — dump จาก client ด้วย Morax หรือใช้ dump จาก AstralOS รุ่นใหม่ (ตัว Go:
protocol/proto/StarRail.proto+ generate ใหม่ด้วย protoc-gen-go) cmd_ids_<ver>.json— mapping cmd ทั้งหมดของรุ่นนั้น (ตัว Go generate cmdid map จาก// CmdID:comments ใน proto อัตโนมัติ)- Data files จาก AstralOS dump (ใช้ได้ — เป็น data จาก client ไม่ใช่โค้ด):
challenge_data.json,res.json,teleports.json,mission_unlocks.json,freesr-data.json BASE_AVATAR_IDS— เพิ่ม avatar ตัวใหม่ (ตัว Go:internal/gameserver/dispatch.go— 89 ตัวใน 4651 มี 1511/1512/1513)versions.json— hotfix URLs ต่อเวอร์ชัน (ดูหัวข้อ 5)- Tutorial ID lists — ตัว Go generate จาก Rust tutorial.rs แล้ว (
handlers_tutorial_ids.go1249+855 ids)
3. Cmd ID และ response layout เปลี่ยนทุกรุ่น
- ScRsp id เลื่อนตำแหน่ง:
StartChallengeScRspเคยเป็น 1775 → 4.6.51 กลายเป็น 1787 และ 1775 ไปเป็นGetChallengeGroupStatisticsCsReq— ส่ง response ผิด id = client รอไม่ได้ หมุนค้างตลอด - Field tag เลื่อน:
StartChallengeCsReq.challenge_idtag 12 → tag 14 — decoder มือต้องอัปเดต - Response body layout เปลี่ยน: tag ของ retcode/scene/lineup ใน ScRsp ต้องแกะใหม่จาก proto ทุกรุ่น
- cmd ที่ไม่มีใน dump (เช่น 1452, Anomaly Arbitration) = เนื้อหาหลัง beta build ที่ client ถือ — ทำไม่ได้จนกว่าจะมี dump ใหม่
- ชื่อ message เปลี่ยนทุกรุ่น: 4.6.51 เปลี่ยน
PveBattleResultCsReq → PVEBattleResultCsReq,QuickStartCocoonStageCsReq → StartQuickCocoonStageCsReq/StartQuickCocoonStageRsp,GateServer → Gateserver,GetGachaInfoCsReq/DoGachaCsReq หายไป— โค้ดต้องผูกกับ proto ของรุ่นนั้นจริงๆ เสมอ
4. Optional message fields — สาเหตุจอดำที่ไล่ยากที่สุด
_CmdGetBasicInfoScRsp NRE วนซ้ำเพราะ PlayerSettingInfo มี optional message ซ่อนหลายตัว (ojpaodihaje Tag 13, ankidjjinei Tag 1171, cmfcooeeecb Tag 1552) — ปล่อย None แล้ว client unwrap → NRE → จอดำ
วิธีไล่: NRE offset ใน Player.log ต่างกัน = field คนละตัว ใส่ทีละตัวจนหมด ใช้ field_overrides_<build>.json ช่วยแปลชื่อ
5. Design data CDN — ต้นเหตุ "ตารางพัง/ค้างหน้าโหลด"
query_gatewayresponse (messageGateserver) มีasset_bundle_url/ex_resource_url/lua_url— มาจากversions.json(sdkserver fetch จาก CDN จริงผ่าน proxy)- client beta hot-update design data จาก URL เหล่านี้ — ถ้า CDN push ชุดใหม่กว่า GameAssembly ที่ติดตั้ง → ExcelTable โหลด FAILED → ค้างหน้าโหลด (ดู D number: 4.6.51 ติดตั้ง = D16700702 ≠ D16706787)
- แก้:
ex_resource_urlต้องชี้design_data/BetaLive/output_<D ที่ตรงกับ client>(ตัวอย่าง:https://autopatchcn.bhsr.com/design_data/BetaLive/output_16700702_...) — ห้ามชี้ /no_update เพราะ client beta ไม่ fallback ใช้ local - ไฟล์ที่โหลดมาแล้วพังค้างใน
StreamingAssets/DesignData/Windows/— restore จาก.bakหรือ re-verify
6. กับดักที่พบจริงใน 4.6.51 (เก็บไว้เจอซ้ำ)
- ทีมเกิน 4 ตัว: client ส่งทีม 2 ช่องทาง (
avatar_lineup_first+first_lineup) รวมได้ 8 ตัว →TeamManagerIndexOutOfRange ตอนเข้า arena — แก้ด้วย dedupe + truncate(4) - InteractProp: เก็บ mapping entity_id → prop ตอนโหลดฉาก แล้วตอบ
prop_stateจริงจาก config — ห้ามตอบinteract_id2(มันคือ interaction id เช่น 1100 ไม่ใช่ state) - GetNpcTakenReward (2174): client ถามก่อนคุย NPC — ไม่ตอบ = dialogue ไม่มีตัวเลือกและค้าง
- Tutorial: mark ทุก id ว่าดูแล้ว (
GetTutorial/GetTutorialGuidestatus enumOekdmamgidc=2) ไม่งั้น popup เด้งวนและบล็อก input;FinishTutorial/UnlockTutorial*ต้องตอบพร้อม Tutorial/TutorialGuide message - GetMissionStatusScRsp ต้องส่ง
finished_main_mission_id_list+curversion_finished_main_mission_id_list(tag 13) — ตอบเปล่า = client Lua NRE → บล็อก input + client ส่ง cmd 1204 ซ้ำรัวๆ - GetSwitchHandData: 4.6.51 CsReq 8115 → ตอบ ScRsp 8108 (tag 3 retcode 0; 8104 คือ SwitchHandStartCsReq ห้ามตอบ)
- arena ใหม่ไม่มีใน client เก่า: challenge phase ใหม่ (เช่น 5312) ใช้ arena 3014101 ซึ่ง client build เก่าไม่มี MapEntrance → Lua
GotoMapParam: attempt to index nil with 'ID'— fallback ไป arena มาตรฐาน 3000101 - Scene ไม่มี teleport: ฉาก arena ไม่มี teleport ใน res — fallback จาก
teleports.jsonหรือ spawn ใกล้ prop แรกหันหน้าเข้าหา - full coverage ตอบ ScRsp ว่างทุก cmd = อันตราย! — client modules (
GridFight,ChessRogue,EvolveBuild,ChimeraDuel,Jukebox) รับว่างแล้วSync(nil)→ NRE วน → จอดำ — cmd ไม่รู้จักต้อง drop เงียบๆ ตอบว่างได้เฉพาะ dummy list ที่ทดสอบแล้ว - BASE_AVATAR_IDS ห้ามใส่ตัวที่ client build นี้ไม่มี (เช่น id จาก reference รุ่นใหม่กว่า) → พังตอน init โลก; ขาดตัวที่มีจริงก็ crash ตอน battle เช่นกัน
- freesr-data.json ฟอร์แมตใหม่ (จาก SRTools): avatars 99 ตัว ไม่มี key leader/lineups — parser ต้อง tolerate ฟิลด์แปลก (Go:
LoadFreesrDataรองรับแล้ว)
7. เครื่องมือและแหล่งข้อมูล
- AstralOS dump:
C:\Users\Phitchayut\Downloads\New folder(bin/.json = data 4651, tools/reference/ = proto/cmdids/field_overrides 4651 แปล obfuscated → ชื่อจริง) — ห้ามก็อปโค้ดทั้งดุ้น ใช้ data ได้ field_overrides_<build>.json— ดัชนีแปลชื่อ field obfuscated → ชื่อจริงของทุก message- Firefly Shelter (
C:\Program Files\Firefly Shelter\server\firefly-go) — PS ภาษา Go ที่เวิร์กกับ client นี้ ปิด source — สำรวจได้จากtools/firefly_dump(symbols: handlers/pb fields/cmds) และ data dir ของมัน (ใช้ freesr-data.json / data-in-game.json / version.json ฟอร์แมตเดียวกับ FreeSR) server_debug.log/gameserver.log— บันทึก ENTER_SCENE/INTERACT_PROP/SCENE_CAST_SKILL + log ทุก cmd- Dimbreath Game Data:
https://gitlab.com/Dimbreath/turnbasedgamedata(ExcelOutput แม่นยำ) - robinsr reference:
https://git.neonteam.dev/amizing/robinsr(อ่านเทียบได้ ห้ามก็อปทั้งดุ้น)
8. กระบวนการทำงานที่พิสูจน์แล้วว่าเร็ว
- ทดสอบ 1 อย่าง → อ่าน log ทันที → แก้ 1 จุด → build → ทดสอบซ้ำ (อย่าแก้หลายจุดโดยไม่มี log ยืนยัน)
- Reference จาก PS อื่น: อ่านเทียบได้ ห้ามก็อปโค้ดทั้งดุ้น — ดึงเฉพาะ data dump และค่า config
- commit ทุกครั้งที่แก้เสร็จ — ไฟล์เคยถูกย้อนกลับโดยไม่ทราบสาเหตุ 2 ครั้ง
- แก้ path hardcode ใน .bat ทุกครั้งที่ย้าย/rename โฟลเดอร์
- เปลี่ยน versions.json ทีละ field ด้วย script — อย่าแก้มือตอนเซิร์ฟเวอร์กำลังรัน
9. สถานะที่รู้ว่ายังไม่สำเร็จ (ณ 4.6.51)
- Anomaly Arbitration — ไม่มี cmd ใน dump ของ client รุ่นนี้
- Currency War (ตระกูล Rogue 101 cmd) — ตอบ default ครบ แต่เล่นได้จริงต้อง implement state เฉพาะ
- Gacha (GetGachaInfo/DoGacha) — proto 4.6.51 เปลี่ยนชื่อ message → ตัว Go skip handler ไว้ (log "handler skipped") ต้องหาชื่อใหม่จาก dump
- StartChallenge กับ challenge_id ที่ไม่มีใน challenge_data.json — ใช้ fallback arena 3014101
- ยังไม่ได้ทดสอบกับ client จริงแบบเต็ม session หลังพอร์ต Go (รอ user ทดสอบ: login → Parlor Car → เดิน → กดตี → วาป → เข้า endgame)
10. โค้ด Rust แตกตัวกับ proto ใน repo (บทเรียนสำคัญ)
commit สุดท้ายของ Rust (ebc27a1) อ้างชื่อ message ที่ ไม่มีใน proto/StarRail.proto ของตัวเองแล้ว:
PveBattleResultCsReq, QuickStartCocoonStageCsReq, GateServer, GetGachaInfoCsReq, GroupRefreshInfo, LineupSlot
→ สาเหตุ: proto ถูกแทนด้วย dump 4.6.51 ใหม่ (ชื่อ obfuscated พิมพ์ใหญ่) แต่ handler ไม่ได้ตาม
→ กฎ: หลังแทน proto ต้อง compile ทันทีและแก้ชื่อให้ตรง ก่อนปล่อย build ให้ user ทดสอบ
→ ตัว Go แก้ครบโดยผูกทุก handler กับชื่อจริงใน proto (handler ไหน proto ไม่มีจะ log+skip ไม่ panic)
11. บัคกล้องหลุดตอนกดตี — SceneCastSkill (แก้แล้วในตัว Go)
อาการ: กดโจมตีใน overworld แล้วกล้องหลุดออกจากตัวละคร
ต้นเหตุ (Rust battle.rs): handler ตอบ battle_info + monster_battle_info ให้เข้า battle กับ target ที่ผ่าน filter หลวม id > 30000 || id < 1000 — ซึ่งรวม avatar entity ของผู้เล่นเอง (1-4); ถ้าอยู่ arena (entry >= 3000000) ยังหลอก target เป็น 30001 เสมอ → client เริ่ม battle transition กับ "monster" ที่ไม่มีจริง → กล้อง battle ไม่มี target ให้ตาม → หลุด
วิธีแก้ (Go internal/gameserver/handlers_battle.go):
- ตอนโหลดฉาก เก็บ monster entity จริง (entity_id → event_id) ไว้ใน
Session.MonsterEntities SceneCastSkillยอมรับเฉพาะ hit target ที่เป็น monster จริง- ไม่มี monster จริง → ตอบ
retcode 0 + cast_entity_idไม่เข้า battle (client เล่นแอนิเมชันตีปกติ กล้องค้างกับตัว) - ตีโดน monster จริง (รวมบอส arena 30001) → เข้า battle ปกติ
- ยืนยันด้วย
internal/gameserver/smoke_test.go(จำลอง client เต็มรูปแบบผ่าน UDP จริง)
บทเรียน: อย่าตอบ battle/transition ให้ client จาก target ที่ server ไม่ได้ spawn เอง — ตรวจกับ entity registry ของฉากปัจจุบันเสมอ
12. Go server (GaC-hkrpg-go) — สถาปัตยกรรมและข้อควรรู้
- mihoyo KCP header 28 ไบต์:
conv(4) + token(4) + cmd(1) + frg(1) + wnd(2) + ts(4) + sn(4) + una(4) + len(4)Little-Endian — standard KCP lib อ่านไม่ได้; Go port อยู่pkg/kcp(port จาก kcp.rs 1:1, UDP loopback test ผ่าน) - นาฬิกา KCP ต้องเป็นมิลลิวินาที — Rust เดิมใช้วินาที (session_time secs) ทำ RTT/resend เพี้ยนทั้งระบบ
- Handshake: 20-byte NetOperation — client
head=0xFF, tail=0xFFFFFFFF, server ตอบhead=0x145, tail=0x14514545(param1=conv, param2=token), ตัดการเชื่อมต่อhead=0x194, tail=0x19419494 - Packet framing ใน KCP stream:
HEAD 0x9D74C714 + cmd(u16 BE) + headLen + bodyLen + head + body + TAIL 0xD7A152C8— ไม่มี XOR encryption ใน build นี้ - cmdid map generate จาก
// CmdID:comments ใน proto →protocol/proto/cmdid.go(regenerate ตอนเปลี่ยน proto) - ใช้ data files Rust PS ได้ทันที (freesr-data.json / persistent / res.json / teleports.json / challenge_data.json / mission_unlocks.json / versions.json) — ฟอร์แมต JSON ตรงกัน รวม enum string อย่าง
main_character: "FemaleHarmony" - Build:
go build -o gameserver-go.exe ./cmd/gameserver+./cmd/sdkserver; ทดสอบ:go test ./...; เปิดด้วยrun_go.bat(UDP 23301 + HTTP 21000 เหมือนเดิม) - ที่ตั้ง skill library:
tools/AI TOOLBOX/(ทั้งสองโปรเจกต์มี copy — อัปเดตต้นฉบับที่เดียวแล้วก็อป)
13. บทเรียนรอบทดสอบ client จริง (2026-10-07 01:00-01:53)
- Req id ≠ ScRsp id — เช็ค reply จาก Cmd enum เสมอ: บันทึกเดิมบอกตอบ
GetSwitchHandDataScRsp = 8104แต่ 8104 คือSwitchHandStartCsReq(Req!) ของจริงคือ 8108 — ส่ง id ผิดฝั่งไปหา client = client มองไม่เห็นคำตอบ - teleport id ซ้ำกันทุกแมพ (1001, 1002, ...): ห้ามค้น teleports.json ทั่วโลกด้วย id เดียว —
วาปแล้วอาจไปผิดภูมิภาค (AA → AP) — ใช้ floor จาก
scene_identifierกำหนดขอบเขตเสมอ (findEntryByTeleportInFloor) - client layout drift: payload ของ cmd เดียวกันอาจ parse ไม่ผ่าน proto ที่ถืออยู่ (GetSceneMapInfo 1461) → raw handler + lenient wire-walk fallback + log hexdump เพื่อแกะ layout จริงจาก log
- challenge_id=0: เมื่อ decoder ตาม tag ไม่เจอ ให้สแกน varint ทุก field หาค่าที่
"หน้าตาเป็น challenge id" (
detectChallengeID— range ต่อโหมด + เช็ค challenge_data) - StartChallengeScRsp 4.6.51 ต้องมี stage_info (tag 13) — boss node buff echo
- arena 3014101 client ไม่มี แม้ res.json จะมี — fallback 3000101 ทั้ง StartChallenge และ Tierce
- GetUnreleasedBlockInfo 4.6.51 = CsReq 4190 → ScRsp 4112 (retcode tag 14) — Endgame entry รอ response นี้ก่อนเปิด UI (บันทึก Rust บอก 4130 ผิด)
- Activity schedule จริง:
tools/march7_dump/Config/Custom/ActivityConfig.json(147 activities จาก client 4.6.51) — อย่าส่ง synthetic id เพราะ Travel Log จะว่าง
14. บทเรียนระบบ Endgame ใน 4.6.51 (Apocalyptic Shadow, Pure Fiction, MoC)
- GetChallengeGroupStatistics (1775/1711 → 1708):
- Client ส่ง
GetChallengeGroupStatisticsCsReq - ScRsp 1708 ต้องใช้ proto 4.6.51 (
group_id=5,retcode=9,challenge_boss=1,challenge_story=3,challenge_default=11) ห้ามใช้ layout 4.5.52
- Client ส่ง
- StartChallengeTierce (Starward/PF/AS) = CmdID 8998 (4.6.51):
- Client ส่ง
StartChallengeTierceCsReq(8986):challenge_id(tag 3),stageIndex(tag 5),lineup_tierce_list(tag 9) - Response ใน 4.6.51 คือ 8998 (
StartChallengeTierceScRsp) ประกอบด้วยretcode(tag 3),scene(tag 9),cur_tierce_challenge(tag 15) - ข้อมูลแมพดึงจาก
cd.Tierce[baseID]สำหรับทุก stageIndex
- Client ส่ง
- SetChallengeTierceLineup (8978/9000 → 8985):
- Callback
OMOEFAHFJFL::EMMGHDLIPOHใน client รอ CmdID 8985 (SetChallengeTierceLineupScRsp) บอดี้[0x08, 0x00] - เซิร์ฟเวอร์เดิมส่ง 8999 ทำให้ client หมุนค้างตลอดเวลาเลือกตัวละคร
- Callback
- Leave Challenge (1705 → 1771, 8989 → 8983) และการรักษาสภาพตัวละคร:
loadChallengeSceneห้ามเขียนทับplayer.Scene.EntryID/FloorID/PlaneID— มิฉะนั้น persistent save จะพังเป็นฉาก arena- เมื่อผู้เล่นกดยกเลิก/ออกจาก Endgame ต้องตอบ ScRsp (1771 หรือ 8983) พร้อมเรียก
loadScene(s, returnEntry, true, nil, nil)สั่งEnterSceneByServerScNotify(1427) ดึงตัวละครกลับ open world ทันที ตัวละครจะไม่หาย
- StartChallenge Lineup List & Stage Info (1787):
- ต้องแนบทั้ง Node 1 (index 0) และ Node 2 (index 1) ใน
LineupList StageInfo(tag 13) แนบเฉพาะเมื่อchallengeID >= 30000 && buff != 0(AS Boss) ห้ามแนบว่างใน MoC และ PF
- ต้องแนบทั้ง Node 1 (index 0) และ Node 2 (index 1) ใน