Files
GaCy-GO/docs/GLM-GACHA-GUIDE.md
T
2026-10-28 18:54:43 +07:00

25 KiB

ข้อมูลและวิธีเพิ่มตู้กาชาให้ GLM — Client Beta ปัจจุบัน

อัปเดต: 24 ตุลาคม 2026 (Asia/Bangkok)

อ่านคู่กับ GLM-BETA-DATA.md และ ZCODE-HANDOFF.md

1. เป้าหมายผู้ใช้และสถานะจริง

ผู้ใช้ต้องการเลือกตู้ตัวละคร โดยแต่ละตู้มีตัวเด่นหนึ่งตัว และผลสุ่มตรงกับตัวที่แสดงบนตู้ ไม่ได้ต้องการแค่หน้า Standard ที่เปิดดูได้แต่สุ่มไม่ได้

ตอนนี้ backend มีโค้ด featured-only sandbox สำหรับสุ่ม 1/10 ครั้ง และจัดการ duplicate material แต่ ยังไม่มี featured banner ที่เปิดใช้งานจริง Policy เปิดเพียง Standard เพื่อหลีกเลี่ยง Client crash จึงยังไม่ตรงคำขอ

เอกสารนี้ให้ข้อมูลที่ตรวจแล้วและลำดับพัฒนาต่อ ไม่ได้อ้างว่าการเพิ่ม path ใน JSON จะทำให้ตู้ใช้งานได้ หรือว่าดึง asset ที่หายกลับมาแล้ว

Client ที่อ้างอิงใน capture นี้: GameAssembly.dll SHA-256

4aa08a0706f27d2cf9951b61920c179140ece9ea94f7e34422c18e46922939b6

เป็น Client Beta .54 ที่ตรวจ runtime จริง ไม่ใช้ hash/RVA ของ CW capture .53 แทน

2. ตารางตู้จริงที่ดึงไว้แล้ว

resources/PreviewGameplayCatalog.json → tables.gacha มี GachaBasicInfoConfigRow ครบ 6 แถวของ capture นี้:

GachaID GachaType SortID PrefabPath ผลตรวจ asset
1001 1 99 UI/Drawcard/GachaPanel/StandardGacha_1001.prefab มี
4001 2 1 UI/Drawcard/GachaPanel/NoviceGacha_4001.prefab มี
2001 11 2 UI/Drawcard/GachaPanelLimited/AvatarGacha_1102.prefab ไม่มี
2002 11 3 UI/Drawcard/GachaPanelLimited/AvatarGacha_1204.prefab ไม่มี
3001 12 4 UI/Drawcard/GachaPanelLimited/LightConeGacha_23001.prefab ไม่มี
3002 12 5 UI/Drawcard/GachaPanelLimited/LightConeGacha_23010.prefab ไม่มี

ชื่อ enum ที่พบใน Lua คือ Normal, Newbie, AvatarUp, WeaponUp การจับคู่เลขข้างบนมาจาก captured rows ไม่ใช่การสร้างเลขเพิ่ม

แถวเหล่านี้ยังเก็บ PoolLabelIcon, PoolLabelIconSelected, PoolName, PoolDesc, TypeTitle และ TextID/hash64 ให้ใช้ original numbers ห้ามอ่านผ่านเครื่องมือที่ทำ uint64 เกิน 2^53 สูญเสีย precision

ข้อจำกัด: แถวในตารางไม่ได้พิสูจน์ว่ามี bundle ใน installation ผล AssetMgr.Exists ยืนยันแล้วว่า limited ทั้ง 4 path ข้างบนไม่มี

หลักฐาน: battle_diagnostics/gacha-asset-exists-20261024.log มีผลครบ 6 path และ END Policy เก็บผลพร้อม hash ไว้ใน resources/GachaSandboxPolicy.json

Seele prefab ที่ไม่มีเคยทำ UIPrefabLoader._HandleLoadResult ArgumentNullException ระหว่าง GachaMainPage._create_pools_panel แล้วออกกลับโลกจอดำ ห้ามเพิ่มกลับ allowlist เพื่อให้หน้าตู้ดูเยอะขึ้นทั้งที่ asset ยังไม่มี

3. Generic/group prefab ที่พบใหม่ — ตรวจแล้วไม่มีเช่นกัน

ถอดจาก bytecode ของ GachaMainPage ปัจจุบัน พบ group presentation ที่มี path จริงดังนี้:

Group enum และค่าที่ capture จริง Path ที่อ้างถึง ผลตรวจใน Client ปัจจุบัน
MultiAvatarUp=1 / MultiWeaponUp=2 UI/Drawcard/GachaPanel/MultiAvatarGacha.prefab ไม่มี
Collaboration=3 UI/Drawcard/CollaborationGacha/CollaborationGacha.prefab ไม่มี
CollaborationMerge=4 UI/Drawcard/CollaborationGacha0440/CollaborationGacha0440.prefab ไม่มี

หลักฐานรอบล่าสุด: battle_diagnostics/glm-beta-targeted-metadata.log

ASSET UI/Drawcard/GachaPanel/MultiAvatarGacha.prefab true false
ASSET UI/Drawcard/CollaborationGacha/CollaborationGacha.prefab true false
ASSET UI/Drawcard/CollaborationGacha0440/CollaborationGacha0440.prefab true false
END

ใน log นี้ boolean แรกคือการเรียกตรวจสำเร็จ ส่วน boolean ที่สองคือผล Exists จึงหมายถึง ตรวจสำเร็จและไม่มี asset ไม่ใช่พบ asset แล้ว

Group path เป็นข้อมูลสำหรับเข้าใจโครงสร้าง UI ไม่ใช่ fallback ที่พร้อมเปิดใช้ อย่าเสนอว่าเปลี่ยนมาใช้ MultiAvatarGacha แล้วจะหายทันที

ผลตรวจ paths แยกเป็น machine-readable data ใน battle_diagnostics/glm-gacha-group-assets.json ส่วนผลอ่าน ตาราง group จริง ล่าสุดอยู่ใน battle_diagnostics/glm-gacha-groups-current.json: BEGIN count=0 จน END count=0 จึงเป็นตารางว่างที่อ่านจบแล้ว ไม่ใช่ยังไม่ได้ capture ห้ามสร้าง GroupID หรือ pool-to-group mapping โดยเอาค่า enum 1–4 มาใช้เป็น IDs

4. การเพิ่มตู้มีสองกรณี

กรณี A: มี config และ asset ของตู้จริงครบใน Client

  1. ใช้แถว GachaBasicInfoConfigRow และ group/type ที่มี provenance ตรงรุ่น ปัจจุบัน group table อ่านครบแล้วเป็น 0 แถว จึงไม่มี original group mapping ให้เปิดใช้ และยังต้องอ่าน type data ที่จำเป็นให้ครบ
  2. ตรวจ prefab, tab icon ทั้งสองสถานะ และ dependency ที่ controller ใช้ ให้มีจริงใน installation
  3. ประกาศตู้ผ่าน resource catalog และ verified policy ไม่ใส่ game ID/path เจาะจงลง handler
  4. ส่ง GetGachaInfo ให้ใช้ ID เดียวกับแถวที่ Client resolve ได้ และ prize/detail data ตรงกับตัวที่หน้าตู้แสดง
  5. ให้ DoGacha ใช้ banner mapping เดียวกัน ตรวจ request/count และจัดการ grant/sync/persistence ให้ครบ
  6. ตรวจ entry/exit และ animation state จาก callback จริง ไม่แก้ด้วยการปิด Canvas หรือกล้องทุกตัว

กรณีนี้ยังทำไม่ได้กับ limited 4 แถวปัจจุบัน เพราะผลตรวจยืนยันว่า prefab ไม่มี

กรณี B: ไม่มี original banner asset — ต้องทำ custom presentation

นี่เป็นทางพัฒนาที่ตรงคำขอได้ แต่ยังไม่มี implementation สำเร็จใน source ปัจจุบัน:

  1. กำหนด extension catalog แยกจาก raw captured tables โดยระบุว่าเป็น custom presentation ไม่ปลอมว่าเป็น original Client data
  2. ใช้ avatar/item metadata และภาพที่ตรวจว่ามีจริงเป็นรายตัว ต้องแสดงชื่อ/ภาพตรงกับผล draw ห้ามใช้ภาพ Standard ค้างไว้แต่สุ่มเป็นตัวอื่น
  3. ทำ UI/controller/binder ที่ใช้ asset ที่มีจริง อาจประกอบจากส่วนย่อยที่ตรวจแล้ว หรือทำ presentation ใหม่ที่เข้ากับระบบ UI; ไม่ใช่เพียงเปลี่ยน Row.PrefabPath เป็น Standard แล้วคาดว่า limited binder จะทำงาน
  4. ต่อ pool/group/tab data และ selection state เข้ากับ Client โดยใช้ชนิด/enum/schema ที่ยืนยันแล้ว เก็บ custom ID mapping ใน resource ที่ตรวจความถูกต้องได้
  5. ให้ Client และ server resolve ตู้เดียวกันจาก catalog เดียวกัน ก่อนรับการสุ่ม
  6. ส่งผล Item/GachaItem/duplicate materials ให้ครบ และคืน transition กลับโลกเมื่อปิด

การส่ง GachaID ใหม่ที่ Client ไม่มี config ไม่พอ: Client ต้องสร้าง/resolve pool, type และ group ได้ด้วย การส่ง GachaID 1001 ซ้ำหลายแถวจะชน key ไม่ได้กลายเป็นตู้แยกทุกตัวละคร

ห้ามใช้สูตร bannerID = avatarID + ค่าคงที่ หรือเดา path ของทุกตัวละครขึ้นเอง แล้วอ้างว่าเป็นตาราง Beta ให้กำหนด custom extension อย่างชัดเจนถ้าจำเป็นต้องสร้าง namespace ใหม่

5. สิ่งที่ Client ใช้สร้างหน้า — มีหลักฐานแล้ว

battle_diagnostics/gacha-main-page.json เป็นผลอ่าน bytecode ที่ไม่ execute ของ:

@BakedLua/Ui/Gacha/GachaMainPage.bytes

_create_pools_panel — prototype 26 / source line 308

ลำดับชื่อที่พบใน bytecode:

GetSortedGachaGroupDatas
IsValid / IsFeatureClosed / IsSinglePoolGachaGroup
DefaultGachaPoolData.PrefabPath
sync_load_prefab
_gachaType2tabItem
create_panel / set_tab_data

แยกสองเส้นทาง:

  • Single-pool group: โหลด DefaultGachaPoolData.PrefabPath แล้วสร้าง tab item ด้วย panel/binder ตาม GachaType
  • Group: เลือกจาก _groupID2GroupPrefab / _groupType2GroupPrefab และ _groupID2TabItem / _groupType2TabItem พร้อม binder ของ group

ไม่ควรดูเพียง GetGachaInfoScRsp แล้วมองข้าม group data ที่หน้า UI ใช้จริง

ฟังก์ชันสำคัญอื่น

  • _init_type2tab_table prototype 8: ผูก Normal, AvatarUp, WeaponUp, Newbie กับ tab item/binder
  • _init_group_tab_prefab prototype 9: กำหนด group prefab/controller/binder maps
  • _get_group_tab_prefab_config prototype 10: เลือก override ด้วย GroupID แล้วจึง GroupType
  • _on_group_tab_change prototype 31 และ _on_tab_change prototype 32: cache selected pool และ setup main page
  • _setup_main_page prototype 33: setup draw/decide/top bar และ Row.PoolName ผ่าน SafeSetTextID

Native/data types ที่ตรวจพบ

BaseGachaPoolData มี:

  • Row getter/setter ชนิด GachaBasicInfoConfigRow
  • TypeBasicInfoRow, PrizeItemDatas, DetailItemDatas
  • SingleDrawData, MultiDrawData, GachaTokenData, DecideData
  • SyncGachaPoolData, _InitPrizeItemsRow, _InitDetailItemsRow, IsValid, IsFeatureClosed, PrefabPath

BaseGachaGroupData มี:

  • _GachaGroupDataRow, _GachaPoolDataList, GroupID, GroupType
  • AddGachaPoolData, GetSortedGachaPoolData, Sort, IsValid, IsFeatureClosed, HasGachaPool

Capture ล่าสุดยืนยัน shape ของ RPG.GameCore.GachaGroupDataRow:

PoolLabelIcon          System.String
PoolLabelIconSelected  System.String
GachaIDList            System.UInt32[]
GroupType              RPG.GameCore.GachaGroupType
GroupID                System.UInt32
FromBinary(reader, GachaGroupDataRow byref)

ต่อจาก metadata ได้ตาม native chain และอ่าน flat GetAll ของตารางนี้ครบแล้ว:

GachaGroupDataRow.FromBinary RVA 0x1e853750
  -> wrapper RVA 0x1e853700
  -> Add dictionary RVA 0x1e853950
  -> flat GetAll RVA 0x1e852f20
BEGIN count=0
END count=0

หลักฐาน ev_551674df9f28f4fc92485a8b8f3490795a8296564ee586fe356b16bca00e0b8b, ข้อมูล battle_diagnostics/glm-gacha-groups-current.json และ assembler files glm-gacha-group-add.asm, glm-gacha-group-dictionary.asm, glm-gacha-group-flat-getall.asm ผูกกับ SHA ปัจจุบัน

ตาราง group ที่ตรวจนี้ว่างจริง ณ capture ไม่ได้แปลว่า Lua/UI ไม่มีโค้ดรองรับ groups แต่ไม่มี GroupIDs หรือ GachaIDList ให้ใช้เป็น original mapping อย่าอ่านค่า enum เป็น GroupID หรือเอาข้อมูลของ reference รุ่นเก่ามาเติมแล้วเรียกว่า Beta ปัจจุบัน

RVAs ข้างบนใช้บันทึกเส้นทางหลักฐานของ assembly hash นี้เท่านั้น ไม่ใช่ค่าที่ให้ฝังใน production MethodHandle/addresses ใน log ใช้ได้เฉพาะโปรเซสที่ capture ห้ามนำ pointer เดิมกลับไปใช้หลังเปิดเกมใหม่

GachaTypeBasicInfoRow มี GachaTypeID, GachaBar, DiamondID, ItemPrice, ItemCosume, UpPropability, BuyPos ต้องอ่านค่าจริงของ type ที่ใช้ ไม่เดาค่า currency/token หรือราคา

Lua modules ที่ควรอ่านต่อแบบเจาะจง

  • Ui.Gacha.GachaPool.DefaultGachaPoolTabItem และ binder
  • Ui.Gacha.GachaPool.DefaultGachaAvatarGroupTabItem และ binder
  • Ui.Gacha.GachaPool.GachaSubAvatarGroupPanel
  • Ui.Gacha.GachaPool.GachaSubAvatarItemIconPanel
  • Ui.Gacha.GachaPool.DefaultGachaAvatarPanel
  • Ui.Gacha.GachaPool.DefaultGachaUpAvatarPanel

UpAvatarPanel มี _init_imgs, _setup_up_avatar_img, setup_view, _on_load แต่การมีชื่อ module ไม่ได้พิสูจน์ว่ามี prefab ที่ใช้งานได้

6. Protocol fields ที่ใช้อ้างอิงได้

ต้นฉบับ protocol/proto/StarRail.proto แถวประมาณ 20415 เป็นต้นไป:

Message Field ที่มีชื่อชัด Wire tag
GachaInfo end_time / gacha_id / gacha_ceiling / begin_time 1 / 2 / 3 / 5
GachaInfo prize_item_list / item_detail_list 10 / 11
GetGachaInfoScRsp retcode / gacha_info_list 1 / 15
DoGachaCsReq gacha_id / gacha_num 4 / 15
DoGachaScRsp retcode / gacha_item_list / gacha_num / gacha_id 1 / 2 / 6 / 13
GachaItem transfer_item_list / is_new / token_item / gacha_item 5 / 7 / 10 / 15

GachaInfo ยังมี fields obfuscated และ GetGachaInfoScRsp มี AKMDBPNEHDE ชนิด NEIMLKNMDBM อย่าอ้างว่าเป็น group payload แน่นอนหรือเติม tag ตามการคาดเดา ต้องตาม native consumer ก่อน

ตาราง command ที่เช็กใน source อยู่ที่ protocol/proto/upstream_cmdids.go:

CommandSourceRevision = 005d2c12db4781071f4b5b68e8f364587886a710
GetGachaInfoCsReq 1974 -> GetGachaInfoScRsp 1919
DoGachaCsReq      1931 -> DoGachaScRsp      1987

เลขนี้ให้เป็นหลักฐานอ้างอิงในเอกสารเท่านั้น implementation ต้องใช้ named resolver ของโปรเจกต์ ห้ามฝังตัวเลขแทนชื่อหรือรับ aliases หลายรุ่น และ source revision ไม่ใช่หลักฐานว่า native schema .54 ทุก field ยืนยันครบแล้ว

7. โค้ด backend ปัจจุบันและการแก้ต่อ

จุดอ่าน catalog

internal/common/gacha_catalog.go:

  • LoadBrowsableGachaBanners ใช้ verified prefabs สำหรับ GetInfo
  • LoadFeaturedGachaBanners ยอมรับเฉพาะ basename AvatarGacha_<avatarID>.prefab ที่อยู่ใน policy
  • ไม่สร้าง GachaID หรือ prefab paths เพิ่มเอง
  • ปัจจุบันพบ Standard ได้ แต่มี AvatarID=0 จึงไม่เปิด featured draw

ข้อควรระวังสำหรับ custom presentation: logic ที่อนุมาน avatar จาก basename ใช้กับ extension catalog ใหม่ไม่ได้เสมอไป ต้องออกแบบ explicit mapping พร้อมตรวจ provenance/asset availability แทนการเปลี่ยน regex ให้รับทุกชื่อ

จุดตอบ GetInfo/Draw

internal/gameserver/handlers_gacha.go:

  • GetInfo ใช้ activity schedule จาก runtime config ส่ง non-null GachaCeiling
  • ถ้ามี featured avatar จึงส่ง prize/detail lists
  • DoGacha ตรวจ banner/count และ reject ระหว่าง Battle
  • ต้องมี avatar ใน imported roster และมี Eidolon resource ที่ตรง
  • duplicate ให้ material ตาม configured first Eidolon cost ไม่ใช่ตัวละคร/ไอเท็มสลับมั่ว
  • ส่ง material delta ใน PlayerSync และ GachaItem ตามจำนวน draws

Persistence ที่ต้องรักษา

GrantGachaDuplicate อ่าน latest freesr-data.json ใต้ shared process/file locks แล้ว merge เฉพาะ materials ไม่สร้าง avatar ใหม่ ไม่เปลี่ยน rank/EX/Traces ไม่เขียน inventory ทับทั้งก้อน

Sandbox policy ปัจจุบัน:

allowed_draw_counts: 1, 10
charge_currency: false
duplicate_reward: configured_first_eidolon_cost

นี่ไม่ใช่ official probability/price model หากจะเปิด acquisition ของ avatar ที่ยังไม่มี ต้องทำ valid avatar/item sync และ targeted persistence เพิ่ม ยังไม่มีเส้นทางนั้นในโค้ดนี้

ไม่ควรเปลี่ยน DoGacha ให้ตอบ success พร้อมผลว่างเพื่อให้ spinner หยุด เพราะจะไม่ตรงจำนวน draws และ state ของ Client

8. อ่าน bytecode ต่ออย่างไร

Client นี้ใช้ xluau.dll มี Luau v4/type1 bytecode ไม่ใช่ LuaJIT:

  • string.dump ไม่มี และ jit.util ใช้ไม่ได้
  • มี debug.getupvalue, debug.getinfo, debug.info, package.loaders
  • รอบที่เก็บ GachaMainPage ใช้ package loader เพื่อรับ closure โดย ไม่ execute closure และไม่แทน package.loaded แล้ว passive hook xluaL_loadbuffer เก็บ bytes
  • อย่าเอา callback/module execution ไปปนกับการอ่านข้อมูล หรือ reload หน้าขณะผู้ใช้สู้

เก็บไว้แล้ว:

battle_diagnostics/gacha-main-page.luau
battle_diagnostics/gacha-main-page.hex
battle_diagnostics/gacha-main-page.json
tools/inspect_luau_v4.py

ตัวอย่างอ่านไฟล์ capture offline จาก root โปรเจกต์:

python tools/inspect_luau_v4.py battle_diagnostics/gacha-main-page.luau --output battle_diagnostics/gacha-main-page.json

Parser อ่าน version/type ที่รองรับเท่านั้น และตรวจว่าใช้ bytes ครบ ไม่ execute chunk เป็นเครื่องมือวิเคราะห์ ไม่ใช่ gameplay test และไม่ใช่ decompiler ที่ยืนยันความหมายทุกคำสั่ง

Layout reference: Luau upstream VM/src/lvmload.cpp, Common/include/Luau/Bytecode.h สำเนาอยู่ใน diagnostics ประกอบการตรวจ format

REA evidence ของ capture: ev_57f147cb094af2060be82f5110e378d25ff2179f0390c56348368a855e7658b8 ขนาด GachaMainPage 23630 bytes ไม่มี message truncation ใน capture นี้

ทุก .lua ใน scripts/ ถูกส่งเข้า Client อัตโนมัติ temporary probes ต้องอยู่ใน tools/ เมื่อไม่ได้ใช้งาน จำกัดเฉพาะ types/functions ที่ระบุและ pause ระหว่าง Battle ห้ามสแกนทุก assembly type/method บน main thread

ณ จุดส่งต่อ probes ที่ใช้รอบนี้ถูกถอดจาก scripts/ และ Frida sessions ปิด/detach แล้ว ไม่ได้ kill Client

9. เช็กลิสต์รับงานต่อ

  • อ่าน package/manifest คู่กับ GLM-BETA-DATA เพื่อไม่ปน .53 และ .54
  • ตัดสินทาง custom presentation โดยยอมรับว่า limited และ generic group prefabs ที่เช็กทั้งหมดไม่มีใน installation นี้
  • ใช้ผล group table ที่อ่านจบแล้ว 0 แถวเป็นข้อจำกัดของ original config; อ่าน type data และ consumer ของ fields obfuscated ที่ยังจำเป็น ก่อนออกแบบ custom mapping
  • ทำ explicit banner/avatar/presentation mapping ให้ Client และ backend ตรงกัน
  • ตรวจ selected portrait/tab/icon/material dependencies เป็นราย resource ไม่เดาชื่อไฟล์
  • รองรับ 1/10 draws และ duplicate/acquisition ตามขอบเขตที่ประกาศ
  • รักษา progression/EX false/material delta และป้องกัน stale session ทับ API
  • ทำ close/exit ให้คืน world/UI transition ไม่ค้างดำ
  • ตามคำสั่งผู้ใช้ล่าสุด: ไม่เพิ่มหรือรัน tests และไม่วนให้ผู้ใช้ลองทีละ handler; ระบุส่วนที่ยังไม่มี Client confirmation ตามจริง

จุดสรุปให้ GLM: Backend ส่วนหนึ่งพร้อมแล้ว แต่การเปิดตู้ตัวละครจริงใน Beta นี้ติด presentation/config mapping และ assets ที่ไม่อยู่ใน installation การมี GachaInfo response หรือเพิ่ม allowlist อย่างเดียวไม่ใช่การทำฟีเจอร์เสร็จ