Files

10 KiB

บันทึกความจำ 2026-10-10: แก้ไข Endgame Modes ทั้ง 7 ข้อ (4.6.51)

ปัญหาที่พบและวิธีแก้ไขครบทั้ง 7 ข้อ

ข้อ 1: กด Apocalyptic Shadow ในแท็บ Latest Update แล้วขึ้น Error(100)

  • สาเหตุ: Client ส่ง cmd 1775 (GetChallengeGroupStatisticsCsReq) ซึ่งโค้ดเดิมใช้ byte slicing แบบ 4.5.52 เก่าที่มี tag ผิดเพี้ยน (group_id=9, retcode=15, challenge_boss=11) ทำให้ client 4.6.51 parse ไม่ได้แล้วโยน Error(100)
  • การแก้ไข: ใน internal/gameserver/handlers_raw.go เขียน challengeGroupStatsReply ใหม่โดยใช้ goproto.GetChallengeGroupStatisticsScRsp ตาม proto 4.6.51 โดยตรง:
    • group_id = 5
    • retcode = 9 (0 = สำเร็จ)
    • AS: challenge_boss = 1
    • PF: challenge_story = 3
    • MoC: challenge_default = 11

ข้อ 2: AS Starward (cmd 8986 StartChallengeTierceCsReq, challenge 30225) เข้าไม่ได้

  • สาเหตุ:
    1. mapChallengeStageID ฝืน map id 30200..30300 ทุกตัวไปเป็น 30204 (ซึ่ง 30204 ไม่มีใน tierce)
    2. decodeStartChallengeTierceReq ตรวจหาเฉพาะ tag 12/14 (4.5.52) แต่ client 4.6.51 ส่ง LineupTierceList ใน tag 9
    3. handleStartChallengeTierce ตรวจสอบ cd.Tierce เฉพาะเมื่อ stageIndex == 2 ทำให้ stageIndex 0 หลุดไปหา 3014101 -> 3000101
    4. Response ตอบด้วย cmd 8973 (4.5.52) แทนที่จะเป็น 8998 (4.6.51)
  • การแก้ไข:
    1. mapChallengeStageID คืนค่า id ทันทีถ้า id มีอยู่ใน cd.Challenges หรือ cd.Tierce (30225 คืน 30225)
    2. decodeStartChallengeTierceReq parse ผ่าน proto.Unmarshal(&goproto.StartChallengeTierceCsReq)
    3. handleStartChallengeTierce ดึงข้อมูลจาก cd.Tierce[baseID] ทุก stageIndex ได้ entrance 3012602, group 7, monster 2034012, event 420554
    4. ตอบด้วย goproto.StartChallengeTierceScRsp ผ่าน CmdID 8998 (และ legacy 8973)

ข้อ 3: AS Normal (cmd 1731 StartChallengeCsReq, challenge 30224) เข้าไม่ได้

  • สาเหตุ:
    1. mapChallengeStageID ฝืน map 30224 -> 30204
    2. decodeStartChallengeReq มี wire slicing bug (buf = buf[len(buf)-len(sub):]; buf = buf[len(sub):]) ทำให้ buf desync และพลาด challenge_id
  • การแก้ไข:
    1. mapChallengeStageID เช็ค cd.Challenges[30224] แล้วคืน 30224 ตรงๆ
    2. decodeStartChallengeReq ใช้ proto.Unmarshal(&goproto.StartChallengeCsReq) + helper readLenDelimAndAdvance
    3. handleStartChallenge ตอบ StartChallengeScRsp (1787) ครบทั้ง 2 Node

ข้อ 4: ออกจาก Endgame แล้วตัวละครหาย

  • สาเหตุ:
    1. loadChallengeScene ไปเขียนทับ player.Scene.EntryID = entryID ทำให้ open world กลายเป็นฉาก arena (3000501/3000101) และถูกบันทึก persistent
    2. handleLeaveChallenge และ handleLeaveChallengeTierce ส่งเฉพาะ ScRsp เก่า (1716, 8997) โดยไม่ส่ง loadScene เพื่อพาผู้เล่นกลับ open world
  • การแก้ไข:
    1. นำบรรทัดเขียนทับ player.Scene ออกจาก handlers_challenge_scene.go
    2. handleLeaveChallenge ตอบ CmdID 1771 (LeaveChallengeScRsp, tag 10 retcode 0 [0x50, 0x00]) แล้วเรียก loadScene(s, returnEntry, true, nil, nil) สั่ง EnterSceneByServerScNotify (1427) ดึงตัวละครกลับฉากเดิม
    3. handleLeaveChallengeTierce ตอบ CmdID 8983 (LeaveChallengeTierceScRsp, tag 13 retcode 0 [0x68, 0x00]) แล้วเรียก loadScene เช่นกัน

ข้อ 5: PF Starward หมุนค้างตอนจัดทีมตัวละคร

  • สาเหตุ: Client ส่ง cmd 8978 / 9000 (SetChallengeTierceLineupCsReq) ซึ่ง callback ใน GameAssembly.dll (OMOEFAHFJFL::EMMGHDLIPOH) รอ CmdID 8985 (SetChallengeTierceLineupScRsp / CmdChallengeTierceType_PMMCIBMKOLD) แต่เซิร์ฟเวอร์ส่ง 8999 ทำให้ client ไม่เคยได้รับ callback และหมุนค้าง
  • การแก้ไข: ใน handleSetChallengeTierceLineup ส่ง CmdID 8985 ด้วยบอดี้ [0x08, 0x00] (tag 1 retcode 0) และแมพทั้ง 8978 และ 9000 ใน rawHandlers

ข้อ 6: PF Normal (challenge 20284) เข้าไม่ได้

  • สาเหตุ:
    1. mapChallengeStageID map 20284 -> 20254
    2. handleStartChallenge ส่ง stage_info (tag 13) เป็นก้อนว่างสำหรับโหมด PF ทำให้ client เกิดปัญหา
  • การแก้ไข:
    1. ไม่ map 20284 เพราะมีใน cd.Challenges[20284]
    2. stage_info (tag 13) จะแนบเฉพาะเมื่อ challengeID >= 30000 && buff != 0 (AS Boss เท่านั้น) ส่วน MoC และ PF จะเป็น nil (ไม่ปล่อย tag 13)

ข้อ 7: MoC Normal (challenge 5612) ค้างหน้าโหลดเหลือ 1 ขีด

  • สาเหตุ:
    1. ทันทีที่เข้าฉาก arena client ส่ง cmd 8115 (GetSwitchHandDataCsReq) แต่เซิร์ฟเวอร์ตอบด้วย cmd 8104 (ซึ่งเป็น CsReq: SwitchHandStartCsReq) ทำให้ client สับสนและค้าง loading notch สุดท้าย
    2. mapChallengeStageID มีการเพี้ยน id 5612
  • การแก้ไข:
    1. แก้ cmd 8115 ให้ตอบ CmdID 8108 (GetSwitchHandDataScRsp, body [0x18, 0x00])
    2. mapChallengeStageID คืน 5612 ตรงๆ จาก cd.Challenges[5612] (entrance 3013801)

ข้อ 8: ค้างหน้าจอโหลด 100% (รูปรถไฟ Astral Express หมุนไม่ไปต่อ)

  • สาเหตุจาก Player.log:
    1. DrinkMaker NRE: Client ส่ง cmd 6995 (GetDrinkMakerDataCsReq) แต่ dispatch.go ตอบกลับด้วย 6988 payload เปล่า (len: 0) เพราะไม่อยู่ใน dangerFamilies ส่งผลให้ DrinkMakerBar._UpdateGameplayData() ใน Unity client เจอก้อน nil แล้ว crash NullReferenceException
    2. MapNPC Astral Express NRE: บันทึก persistent ถูกตั้งไว้ที่ entry_id: 100000104 (Astral Express Parlor Car P10000_F10000000) ซึ่งใน client 4.6.51 นี้ ไฟล์ config ของ Chapter00 มีปัญหา (TerrainStageLodConfig ... 没找到) และ MapNPC:227-400001 ไม่มี ConfigEntityPath ทำให้ AsyncLoad MapObject โยน NullReferenceException ระหว่างขั้นตอน AdventurePhase._EnterStep2() ค้างโหลด 100% แล้วแอปเด้งปิด
  • การแก้ไข:
    1. เพิ่ม "DrinkMaker" ลงใน dangerFamilies และเพิ่ม CmdID 6995, 6988 ใน isDangerCmd (dispatch.go) ให้ drop เงียบๆ ป้องกัน empty ScRsp
    2. เปลี่ยน fallback ของ GetCurSceneInfo ใน handlers_scene.go และ handleLeaveChallenge / handleLeaveChallengeTierce ใน handlers_challenge_flow.go จาก 100000104 เป็น 102020107 (Belobog Administrative District P10202_F10202001) ซึ่งเป็นแมพสมบูรณ์ 100%
    3. รีเซ็ตไฟล์ persistent กลับเป็นแมพ 102020107 พิกัด x: -92, y: 4278, z: 68083, rot_y: 155566

ข้อ 9: Run.bat เปิดไม่ติด (หน้าต่างกระพริบปิดทันที)

  • สาเหตุ:
    1. ในคำสั่ง if (...) ของไฟล์ batch มีข้อความ echo ... (Option 4). ซึ่งตัวอักษร ) ไปปิดบล็อก if ก่อนกำหนด ทำให้เครื่องหมายจุด . กลายเป็นคำสั่งที่ผิดไวยากรณ์ DOS/Windows (. was unexpected at this time.) ส่งผลให้ cmd แครชปิดหน้าต่างทันทีตั้งแต่บรรทัดแรกๆ
    2. การใช้ข้อความภาษาไทยในไฟล์ .bat ที่บันทึกแบบ UTF-8 ทำให้ cmd.exe ใน codepage เริ่มต้นของ Windows ตีความไบต์ผิดพลาด
  • การแก้ไข:
    1. แก้ไข run.bat และ run_go.bat ให้เป็น ASCII ที่สะอาด ไม่มีวงเล็บซ้อนในบล็อก if
    2. เพิ่มตัวเลือกแบบยืดหยุ่น:
      • [1] เปิด Server + ตัวเกม / Proxy (Default)
      • [2] เปิด Server ทั้งสองตัว (SDK + Game Server)
      • [3] เปิดเฉพาะ SDK Server ONLY (HTTP 21000)
      • [4] เปิดเฉพาะ Game Server ONLY (UDP 23301)
      • [5] เปิด Server + Gay Proxy
      • [6] Build / Rebuild binaries (Go)
      • [7] ออก
    3. ถ้าเปิดสำเร็จโดยไม่มี error จะนับถอยหลัง 5 วินาทีแล้วปิดหน้าต่าง Launcher ไปเองตามที่ผู้ใช้ต้องการ (และหากมี error จะ pause ค้างไว้ให้อ่าน)