116 lines
18 KiB
Markdown
116 lines
18 KiB
Markdown
# Battle investigation — 2026-10-16
|
||
|
||
## หลักฐานที่ใช้อ้างอิง
|
||
|
||
- Upstream: [HonkaiSlopRail/pearl-sr](https://git.xeondev.com/HonkaiSlopRail/pearl-sr)
|
||
- ตรวจ remote ใหม่แล้วที่ commit `005d2c12db4781071f4b5b68e8f364587886a710`
|
||
- ไฟล์หลัก: `gameserver/src/services/battle.zig`, `gameserver/src/manager/battle_mgr.zig`, `gameserver/src/data/game_config.zig`, `protocol/src/protocol.pb.zig`
|
||
- Client ล่าสุดยัง NRE ที่ `CreateBattleServerCheckResult` ผ่านตัวแปลง BattleAvatar/รายการ relic และ `NMBMHCNGCFG.BDNFEMGIEIH`
|
||
- Payload ที่บันทึกเวลา 21:34 มีตัวละคร 4 ตัว แต่ละตัวมี relic 6 ชิ้นและ sub-affix 24 ค่า แม้เลิกส่ง inventory UniqueId ใน BattleRelic แล้ว Client ก็ยังล้ม
|
||
|
||
## เทียบทีละส่วน
|
||
|
||
| ส่วน | สิ่งที่ตรวจพบ | ข้อสรุปที่ใช้ได้ |
|
||
| --- | --- | --- |
|
||
| Quick Calyx response | Field numbers: retcode=1, battle_info=5, wave=8, cocoon_id=11 ตรง upstream | ยังไม่พบ field-number mismatch ใน wrapper |
|
||
| SceneBattleInfo | ตรวจชุด field numbers ของ 20 fields ตรงกัน | ชื่อ `JAOIOKBBMFC` ของ Go คือ field 7 ซึ่ง upstream เรียก monster_wave_length |
|
||
| BattleAvatar | ตรวจชุด field numbers ของ 18 fields ตรงกัน | ยังต้องดูค่าที่ parser ของ Client ถอดได้จริง |
|
||
| Relic/lightcone | BattleRelic, RelicAffix และ BattleEquipment มีชุด field numbers ตรงกัน | จำนวน sub-affix ฝั่ง Go อย่างเดียวไม่ยืนยันว่า Client ถอดได้ครบ |
|
||
| Monster/wave | SceneMonster, SceneMonsterWave, SceneMonsterWaveParam มีชุด field numbers ตรงกัน | ยังไม่มีหลักฐานให้สลับ WaveParam เป็น nil อีก |
|
||
| Buff | Go ใช้ชื่อ OwnerId แต่ upstream ใช้ owner_index บน field 3 เหมือนกัน | ชื่อ Go ไม่ใช่หลักฐานว่า field บน wire ผิด |
|
||
| แหล่งข้อมูล | Go ใช้ freesr-data.json/persistent; PearlSR ใช้ config.json/selected avatars | Payload ทั้งสองยังไม่เท่ากันในระดับค่าข้อมูล |
|
||
| EX rank | Upstream append รายการ ExRankAvatarID ให้ทุก battle avatar; Go ส่งรายการตามตัวละครที่ปลดล็อก | เป็นข้อแตกต่างที่บันทึกไว้ ยังไม่ชี้ว่าเป็นสาเหตุ NRE |
|
||
| Global/technique buffs | เจ้าของบัฟ, target list และ dynamic values บางตัวต่างกัน | ต้องตรวจแยกหลังระบุตำแหน่งที่ parser/ตัวตรวจล้ม |
|
||
|
||
การตรวจ field numbers ข้างต้นไม่ได้ยืนยัน default values, Client schema ของรุ่นที่รันจริง หรือผลการคำนวณค่าสถานะใน Client
|
||
|
||
## ขั้นแรกที่เพิ่มในรอบนี้: เก็บ wire และอ่านกลับบน Client
|
||
|
||
บันทึกหลังส่ง response สำเร็จจาก Session.Send แทนการบันทึกระหว่างสร้าง battle:
|
||
|
||
- `battle_last_response.bin`: protobuf body ของ response ที่ส่งจริง
|
||
- `battle_last.bin`: bytes ของ nested battle_info ที่ตัดออกจาก response เดิมโดยตรง ไม่ marshal ใหม่
|
||
- `battle_last.json`: ข้อมูลสำหรับอ่านด้วยตา
|
||
- `battle_last_capture.json`: CmdID, ชื่อ response, StageID, เวลา UTC และ SHA-256
|
||
- `battle_diagnostics/`: snapshot ของแต่ละรอบ เก็บทั้งข้อมูลเดิมและรอบต่อมา
|
||
|
||
เปิด read-only Client probe ด้วย `battle_diagnostics.client_battle_type` ใน local config ชื่อ class ต้องเอาจาก stack trace ของ Client รุ่นที่กำลังใช้งาน ไม่ฝังชื่อ class ที่เปลี่ยนตามเวอร์ชันใน Go/Lua
|
||
|
||
Probe ถูกส่งครั้งเดียวใน heartbeat หลัง response battle โดยใช้ protobuf parser ของ Client อ่าน bytes เดียวกับ capture จากนั้นเขียน `[GAC_BATTLE_WIRE]` ลง Player.log พร้อม decoded JSON และ descriptor field numbers ไม่แก้ตัวละคร relic บัฟ หรือ callback การต่อสู้
|
||
|
||
หาก parser/Reflection ไม่ถูกเปิดให้ Lua ใช้ จะมี `probe_error` ใน Player.log แทน ห้ามตีความว่า capture ฝั่ง Go ผ่านแล้วแปลว่า Client decode ผ่าน
|
||
|
||
## ข้อสรุปที่ถอนออก
|
||
|
||
Stack trace เดิมเพียงอย่างเดียวไม่ได้พิสูจน์ว่า WaveParam=nil หรือ SceneMonster.ExtraInfo=nil เป็นสาเหตุ การใส่ ExtraInfo หรือการลบ UniqueId ยังไม่ได้ทำให้การเข้า battle สำเร็จ จึงไม่ถือเป็นการแก้ที่ยืนยันผลแล้ว
|
||
|
||
## ผลตรวจรอบ 21:47: main affix ที่ไม่มีในตาราง
|
||
|
||
Calyx และ overworld ตายที่ stack เดียวกัน ส่วน probe รุ่นแรกทำงานแล้วแต่รายงาน `protobuf parser property not found` จึงไม่มี Client readback และยังใช้ผลของ probe ยืนยันการ decode ไม่ได้
|
||
|
||
แก้การอ่านตำแหน่ง exception: `0xc4eb77c` เป็น return address หลังคำสั่ง `call` ที่ `0xc4eb777` เช่นเดียวกับ frame `0x166b23d2` ที่อยู่หลัง `call 0xc4eb0c0` ดังนั้นจุดนี้มาจาก branch หลัง lookup **main affix** ไม่พบ (`0xc4eb2b0`–`0xc4eb2b3`) ไม่ใช่ branch ตรวจ sub-affix array ที่กระโดดไป `0xc4eb77c` แล้วมี return address `0xc4eb781` การตีความ sub-affix array เป็น null ในรอบก่อนจึงถูกถอนออก
|
||
|
||
ตรวจ payload จริงและ inventory พบรายการนี้:
|
||
|
||
| Inventory UID | Avatar | Relic | main_affix_id เดิม | ผลตรวจ |
|
||
| --- | --- | --- | --- | --- |
|
||
| 483 | 1213 (Dan Heng IL) | 63095 | 11 | ไม่มีใน main affix group 55 ของลูกแก้ว |
|
||
|
||
เทียบกับ [relics.json](https://github.com/Mar-7th/StarRailRes/blob/dbe8cdfcfb0bf657b9fe1cf92d5537f118ccb487/index_new/en/relics.json) และ [relic_main_affixes.json](https://github.com/Mar-7th/StarRailRes/blob/dbe8cdfcfb0bf657b9fe1cf92d5537f118ccb487/index_new/en/relic_main_affixes.json): group 55 มี ID 1–10 โดย ID 10 คือ `ImaginaryAddedRatio` และ `resources/AvatarConfig.json` ระบุ Avatar 1213 เป็น Imaginary
|
||
|
||
แก้ **ข้อมูล inventory เท่านั้น** จาก 11 เป็น 10 ใน `freesr-data.json` ไม่มีเงื่อนไขพิเศษสำหรับ Avatar/Relic นี้ใน converter สำรองข้อมูลและ log ก่อนแก้ไว้ใน `battle_diagnostics/main-affix-repair/` แล้ว และปิด probe ใน local config ขณะลองผลการแก้ข้อมูล
|
||
|
||
ข้อจำกัดของชุดข้อมูลอ้างอิง: ไม่พบ relic ใหม่ 63295/63296 ของ Avatar 1511 จึงไม่ได้รับรอง inventory ทุกชิ้น แต่ชิ้นที่อยู่ในทีมของ payload รอบนี้มีครบและพบ main affix ผิดหนึ่งชิ้นข้างต้น
|
||
|
||
## ขั้นต่อไปหลังแก้ main affix
|
||
|
||
1. เปิดเกมใหม่ให้ Client โหลด inventory ที่แก้แล้ว และลอง Calyx/overworld ด้วยทีมเดิม
|
||
2. หากยังล้ม ให้เก็บ stack ใหม่และเทียบ capture ว่าส่ง main affix 10 แล้ว ก่อนเปลี่ยนส่วนอื่น
|
||
|
||
## แนวทางตรวจเพิ่มเติมหากยังล้ม
|
||
|
||
1. เทียบ SHA-256 ให้ตรงกับรอบที่ Client ล้ม
|
||
2. เทียบ schema/ข้อมูล BattleAvatar และ nested relic ที่ Client ถอดได้กับ capture ฝั่ง Go
|
||
3. หากข้อมูลตรงกัน ให้แยกตัวละครและอุปกรณ์ที่ทำให้ตัวตรวจค่าสถานะล้มโดยมี control payload ก่อนแก้ resource หรือสูตรบัฟ
|
||
4. เปลี่ยนเฉพาะส่วนที่มีหลักฐาน แล้วตรวจ Calyx และ overworld battle แยกกัน
|
||
|
||
สถานะ: ยังไม่มีการยืนยันว่าเข้า fight ได้
|
||
|
||
## รอบ 17 ตุลาคม 2026: แก้เส้นทางร่วมและรอผล Client
|
||
|
||
ตรวจ source ปัจจุบันก่อนแก้แล้วพบดังนี้:
|
||
|
||
1. `HitMonsterBattleInfo` ไม่ได้ตั้ง `monster_battle_type` จึงส่งค่า NONE=0 แทน TRIGGER_BATTLE=1 (อ้างอิง enum ใน `protocol/src/protocol.pb.zig` ของ revision ที่ระบุข้างต้น) แก้ให้ระบุ enum ชัดเจน
|
||
2. เพิ่มการอ่าน `assist_monster_entity_info.entity_id_list` ควบคู่รายการเป้าหมายอีกสองช่อง ยังคงตรวจว่าเป็นมอนที่ส่งในฉากจริง และบันทึก incoming IDs, รายการมอนในฉากและผลจับคู่ ไม่มีการสร้างเป้าหมายปลอม
|
||
3. `res.json` ชุดปัจจุบันมี `avatarConfigs` ว่าง ตัวโหลดเดิมไม่ได้ใช้ `AvatarConfig.json` จึงหา entry attack buff ไม่ได้ เพิ่มการโหลดธาตุจากตารางนี้และ mapping ธาตุจาก resource กฎ Battle
|
||
4. ปรับ owner, targets และ dynamic values ของบัฟตาม PearlSR รวมถึง entry attack สำหรับ Calyx แยก invasion ของ challenge ออกจาก custom stage และสร้าง map ก่อนใส่ battle targets
|
||
5. กฎเฉพาะตัวละครแยกเป็น `resources/BattleRules.json` ระบุ revision ต้นทาง รองรับการเปิด/ปิด global passive จาก persistent ตามเดิม
|
||
6. แปลง Battle SP จากสัดส่วน Cur/Max เป็นสเกล 10000 และเลิกบังคับ Monster MaxHP=10000 เมื่อค่าต้นทางเป็นศูนย์
|
||
7. ลบการเดา Material IDs ด้วย 130000+AvatarID ซึ่งตรงกับ `TryCreateItemData fail` ใน Client และกัน automatic empty response ของ RogueTourn/ChenLing ที่มี NRE ใน log
|
||
|
||
ข้อจำกัด: log ตีมอนล่าสุดก่อนแก้แสดงว่าไม่พบเป้าหมายที่ผ่าน filter แต่ไม่มี incoming ID list จึงยังไม่ทราบเหตุที่จับคู่ไม่ผ่าน ส่วน capture Calyx ที่มีอยู่เป็นข้อมูลวันที่ 16 ต.ค. ไม่ใช่ทีมที่นำเข้าภายหลัง ชุด build รอบนี้เพิ่มข้อมูลที่จำเป็นต่อการแยกสองกรณีนี้
|
||
|
||
การคัดลอก config.json จาก PearlSR ไม่ได้ทำให้ Go อ่านไฟล์นั้น Go ยังคงใช้ freesr-data.json/persistent สำหรับทีมและ custom battle การใช้ custom stage กับดอกไม้มีในต้นฉบับเช่นกัน จึงไม่ถือเป็น regression ด้วยเหตุผลนั้นเพียงข้อเดียว
|
||
|
||
Build ผ่านและวาง gameserver-go.exe ที่ root แล้ว เปิด SDK/Game server พร้อมคอนโซลเพื่อให้ผู้ใช้ลองทั้ง Calyx และ overworld ผลใน Client ยังรอยืนยัน ไม่มีการแก้ข้อมูลทีมใน freesr-data.json
|
||
|
||
### ผลลองจริง 11:53 และการแก้หน้าตัวละคร
|
||
|
||
- 11:53:08 `SceneCastSkill` รับ assist target 30007 และจับคู่กับมอนในฉากได้ ส่ง response พร้อม BattleInfo ขนาด 2286 ไบต์ snapshot SHA-256 battle `f9bb3cf79debf6678f9f466e917e6625e87793aa5fbaf275d90a3f2f39a1fe94` stage 30511021
|
||
- 11:53:16 Client ส่ง PVEBattleResultCsReq และผู้ใช้ยืนยันว่าเข้าสู้ได้แล้ว ยังไม่ระบุผลดอกไม้โดยตรง
|
||
- พบตัวขวางการลองเพิ่ม: เปิด C แล้วติดหน้าตัวละคร Log มี `_SyncPlayerBoardData`, Inventory `_AddUniqueItems`/`_AddItem` และ GetItemsInTypes
|
||
- เทียบต้นฉบับพบ field 4 ของ GetPlayerBoardDataScRsp เป็น unlocked_personal_card_list แต่ Go ส่ง ID รูปโปรไฟล์; field 9 เป็น assist_avatar_id_list แต่ Go ส่ง ID การ์ด อีกทั้งไม่ได้ตั้ง HeadFrameInfo และ DisplayAvatarVec
|
||
- ใน assembly จุด return address 0xdd02ad8 อยู่หลัง throw ที่ 0xdd02ad3 ซึ่งรับ branch จากการตรวจ ItemData+0x18 เป็น null ที่ 0xdd02841 สอดคล้องกับการส่ง ID ของผิดประเภท ยังไม่อ้างว่าเป็นข้อมูลอาวุธ/relic เสีย
|
||
- แก้ profile fields ให้ตรง schema, อ่าน head icons จาก AvatarPlayerIcon.json และ owned cosmetics จาก ProfileConfig.json ที่ระบุ revision เติม ArchiveData จากข้อมูลผู้เล่นแทน null และไม่ตอบว่างให้ SocialPlay/PetMarble ซึ่งพบ exception เพิ่มในรอบใหม่
|
||
- Build รอบโปรไฟล์ผ่านและแทนที่ root executable พร้อมเปิดเซิร์ฟเวอร์ใหม่แล้ว ผลหน้าตัวละครรอเปิดเกมใหม่ยืนยัน
|
||
|
||
ผู้ใช้ชี้แจงผลสำเร็จว่าเป็น Stagnant Shadow สำหรับวัสดุเลื่อนขั้นที่เรียกมอนออกมาจากเงาแล้วตี ไม่ใช่ผลทดสอบ Calyx หรือมอนทั่วไปทั้งหมด คง Calyx เป็นรายการที่ยังรอตรวจ
|
||
|
||
หลังเปิดเกมรอบ 11:56 ผู้ใช้ยืนยันอาการหน้าตัวละครหายแล้ว และ Player.log รอบใหม่ไม่พบ PlayerBoard/Inventory/Archive exceptions เดิม ยังพบ `_OnGetCurBattleInfoScRsp` ตอน login หนึ่งครั้ง ซึ่งต้องแยกจากผลสำเร็จของการตีมอน และยังไม่มี Calyx request ใหม่ใน log รอบนี้
|
||
|
||
### ยืนยันผลท้ายรอบ
|
||
|
||
ผู้ใช้ตอบคำถามแยกเฉพาะ Calyx ว่า **Calyx เข้าได้แล้ว** จึงยืนยันผลลองใน Client ได้ทั้ง Calyx, Stagnant Shadow ผ่าน SceneCastSkill และการเปิดหน้าตัวละคร หลักฐาน Calyx ในส่วนนี้เป็นคำยืนยันโดยตรงของผู้ใช้ ไม่ใช่การอนุมานจาก build หรือจาก log ของ Stagnant Shadow ผลยังไม่ครอบคลุมโหมด challenge/มอนทั่วไปทุกชนิด และ login GetCurBattleInfo exception เป็นปัญหาที่บันทึกแยกไว้
|
||
|
||
ตรวจ log หลังคำยืนยันพบ Calyx request cmd 1305 เวลา 11:56:29 และ response cmd 1371 ขนาด 2279 ไบต์ capture เป็น StartQuickCocoonStageRsp stage 30511021, battle SHA-256 `092528d29fc9bdfc025b89d8904d8a4f3ca706bb2cf777726b03f5a29d9233f7` จึงมีหลักฐาน response ของรอบเดียวกันประกอบคำยืนยันด้วย
|