Files
GaCy-GO/docs/scratch/lunarcore_architecture_deep_dive.md
T

38 KiB

รายงานการวิเคราะห์สถาปัตยกรรมและการทำงานของ LunarCore (Deep-Dive Technical Specification)

เอกสารอ้างอิงทางเทคนิคระดับลึกสำหรับวิศวกรซอฟต์แวร์และโมเดลปัญญาประดิษฐ์ (AI Systems)
วิเคราะห์จากซอร์สโค้ดของโครงการ: Melledy/LunarCore
ครอบคลุมสถาปัตยกรรมระบบ, Binary Network Protocol Framing, State Machine, Authoritative vs Client Simulation, การจัดการข้อมูล (Data & Persistence) และ Gameplay Subsystems ทั้งหมด


สารบัญ (Table of Contents)

  1. ภาพรวมสถาปัตยกรรมระดับสูง (High-Level Architecture)
  2. เลเยอร์เครือข่ายและการเข้ารหัสแพ็กเก็ต (Network & Protocol Framing)
    • 2.1 วงจร HTTP / Dispatch / Gateway Server
    • 2.2 โครงสร้าง Binary Packet Framing (Magic & Header Layout)
    • 2.3 กลไก KCP Transport และ GameSession State Machine
  3. วงจรชีวิตของผู้เล่นและการเชื่อมต่อ (Player Lifecycle & Handshake Flow)
  4. โครงสร้างข้อมูลและฐานข้อมูล (Persistence & Morphia ODM)
    • 4.1 Internal MongoDB vs Standalone MongoDB
    • 4.2 การแบ่งแยก Collections และ Document Schema
  5. ระบบจัดการทรัพยากรเกมและตารางข้อมูล (Game Data & Resource Pipeline)
  6. ระบบฉากและ Entity ในโลกเปิด (Scene & Entity Engine)
  7. ระบบการต่อสู้ (Battle Orchestration & Reconciliation)
  8. ระบบโหมดการเล่นขั้นสูง (Simulated Universe & Challenge Modes)
  9. ระบบตัวละคร, คลังสัมภาระ และไอเทม (Avatar & Inventory Subsystem)
    • 9.1 Multi-Path Switching (ระบบเปลี่ยนคลาส Trailblazer)
    • 9.2 ระบบสุ่มค่า Relic (Main Affix & Sub Affix Step Generator)
  10. ระบบ Gacha และคำสั่งเซิร์ฟเวอร์ (Gacha, Commands & Plugins)
  11. สรุปข้อสังเกตและคู่มือการนำไปพัฒนาต่อสำหรับ AI

1. ภาพรวมสถาปัตยกรรมระดับสูง (High-Level Architecture)

LunarCore ถูกออกแบบเป็น Server Reimplementation ในภาษา Java (JDK 17+) สำหรับเกม Honkai: Star Rail โดยแบ่งบทบาทการทำงานออกเป็น 2 โดเมนหลัก ในระบบเดียว (สามารถรันแยกกระบวนการหรือรันพร้อมกันได้ผ่านแฟล็ก -dispatch และ -game):

  1. Dispatch / HTTP Server (พอร์ตเริ่มต้น: TCP 80 หรือ 443 ผ่าน SSL):

    • ทำหน้าที่จำลองระบบ SDK ของ miHoYo / HoYoverse: การลงทะเบียน, ล็อกอิน (OAuth/Password/Cached Token), ตรวจสอบความเสี่ยง (Risk check), ส่งคืนรายการภูมิภาค (Region List) และข้อมูลเกตเวย์ (query_gateway)
    • ใช้เฟรมเวิร์ก Javalin (ครอบบน Jetty Server) มีประสิทธิภาพสูงและมี footprint ต่ำ
  2. Game Server (พอร์ตเริ่มต้น: UDP 23301 ผ่าน KCP Protocol):

    • รองรับการเชื่อมต่อแบบ State-full ด้วย KCP protocol over UDP (ใช้ไลบรารี kcp.highway.Ukcp บนพื้นฐานของ Netty buffer)
    • จัดการ State ของผู้เล่น, ฉาก (Scene), ตำแหน่งในแผนที่ (Floor/Position), คำสั่งในเกม (Commands), และระบบการต่อสู้ (Battle Setup & Result Validation)
  3. Core Dependencies & Libraries:

    • Quickbuf (us.hebi.quickbuf): เอนจิน Protobuf ประสิทธิภาพสูงพิเศษระดับ zero-allocation ทำงานเร็วกว่าและกินแรมน้อยกว่า Google Protobuf Java มาตรฐาน
    • Morphia (dev.morphia): Object-Document Mapper (ODM) สำหรับติดต่อ MongoDB
    • Mongo Java Server (de.bwaldvogel): เซิร์ฟเวอร์ MongoDB จำลองในตัวที่ทำงานบนหน่วยความจำหรือ H2 Database Backend ทำให้เซิร์ฟเวอร์รันได้ทันทีโดยไม่ต้องติดตั้ง MongoDB ภายนอก
    • FastUtil (it.unimi.dsi.fastutil): ไฮสปีด Primitive Collections (Int2ObjectMap, IntSet, IntList) เพื่อลดภาระ Garbage Collection (GC)
    • Reflections (org.reflections): สแกนหา Controller, Packet Handlers, และ Commands แบบอัตโนมัติ
flowchart TB
    subgraph Client["Honkai: Star Rail Game Client"]
        ClientHTTP["Client Web/SDK Layer"]
        ClientKCP["Client KCP/UDP Transport"]
    end

    subgraph LunarCoreServer["LunarCore Runtime"]
        subgraph HTTPServer["HTTP / Dispatch Server (Javalin/Jetty)"]
            AuthRoutes["/mdk/shield/api/login<br/>/combo/granter/login"]
            QueryDispatch["/query_dispatch (Region List)"]
            QueryGateway["/query_gateway (Base64 Proto)"]
        end

        subgraph KcpGameServer["Game Server (KcpServer / Netty UDP)"]
            KcpListener["GameServerKcpListener"]
            Session["GameSession (State & Crypto)"]
            PacketRouter["GameServerPacketHandler (CmdId Dispatch)"]
            
            subgraph Subsystems["Core Game Services"]
                SceneMgr["Scene & Entity Manager"]
                BattleSvc["Battle Service (Setup/Validate)"]
                RogueSvc["Simulated Universe (Rogue Engine)"]
                GachaSvc["Gacha Service (Hot-reloaded)"]
                InvSvc["Inventory & Relic Service"]
            end
        end

        subgraph DataLayer["Persistence & Resources"]
            Morphia["Morphia ODM"]
            MongoDB[("MongoDB / Internal H2 Mongo")]
            GameData["GameData / StarRailData (JSON Excels)"]
        end
    end

    ClientHTTP -->|"HTTP GET/POST"| HTTPServer
    ClientKCP <-->|"KCP UDP Packets"| KcpListener
    KcpListener --> Session
    Session --> PacketRouter
    PacketRouter --> Subsystems
    HTTPServer --> Morphia
    Subsystems --> Morphia
    Morphia --> MongoDB
    Subsystems --> GameData

2. เลเยอร์เครือข่ายและการเข้ารหัสแพ็กเก็ต (Network & Protocol Framing)

2.1 วงจร HTTP / Dispatch / Gateway Server

ลำดับการเรียกจากไคลเอนต์ก่อนเข้าเกม:

  1. GET /query_dispatch: ไคลเอนต์ขอรายชื่อเซิร์ฟเวอร์
    • เซิร์ฟเวอร์ตอบกลับด้วย Protobuf DispatchRegionData เข้ารหัสเป็น Base64 String บรรจุรายชื่อ Gateway, ชื่อภูมิภาค, Dispatch URL
  2. POST /hkrpg_global/mdk/shield/api/login หรือ POST /hkrpg_global/mdk/shield/api/verify:
    • ไคลเอนต์ส่งบัญชี/รหัสผ่าน หรือโทเค็นเดิม
    • เซิร์ฟเวอร์สร้าง Account (ถ้าเปิด autoCreateAccount) และสร้าง sessionKey (SHA-512 Hash)
  3. POST /hkrpg_global/combo/granter/login/v2/login:
    • ไคลเอนต์นำ sessionKey มาแลกเป็น combo_token
  4. GET /query_gateway:
    • ไคลเอนต์ส่งคำขอ Gateway Info สำหรับการเชื่อมต่อไปยังเกมเซิร์ฟเวอร์
    • เซิร์ฟเวอร์ตอบกลับด้วย Protobuf Gateserver เข้ารหัส Base64 String มีฟิลด์สำคัญ:
      • ip: IP ของ Game Server
      • port: KCP Port (ค่าตั้งต้น: 23301)
      • asset_bundle_url, ex_resource_url, lua_url, ifix_url: URL สำหรับ Hotfix ทรัพยากรเกม

2.2 โครงสร้าง Binary Packet Framing (Magic & Header Layout)

ทุกแพ็กเก็ตที่วิ่งผ่าน KCP ระหว่าง Client และ GameServer มีโครงสร้างแบบ Binary Framing ชัดเจนความยาวรวมขั้นต่ำ 16 ไบต์ (Header 12 ไบต์ + Tail 4 ไบต์):

+-----------------------------------------------------------------------+
| Offset (Bytes) | Type       | Field Name        | Description         |
+-----------------------------------------------------------------------+
| 0x00 - 0x03    | uint32 BE  | HEADER_CONST      | 0x9D74C714 (Magic)  |
| 0x04 - 0x05    | uint16 BE  | cmdId / Opcode    | คำสั่ง CmdId (เช่น 11) |
| 0x06 - 0x07    | uint16 BE  | head_len          | Header Length (มักเป็น 0)|
| 0x08 - 0x0B    | uint32 BE  | data_len          | ขนาดไบต์ของ Protobuf|
| 0x0C - ...     | byte[]     | proto_data        | Protobuf Payload    |
| ... - End      | uint32 BE  | TAIL_CONST        | 0xD7A152C8 (Magic)  |
+-----------------------------------------------------------------------+
  • Magic Header: 0x9d74c714 (Big-Endian)
  • Magic Tail: 0xd7a152c8 (Big-Endian)
  • Overhead: คงที่ 16 ไบต์ (ไม่รวมขนาด Payload)
// โครงสร้างการเขียนไบนารีใน BasePacket.java
byte[] packet = new byte[16 + protoSize];
ProtoSink output = ProtoSink.newInstance(packet, 0, packet.length);
this.writeUint32(output, 0x9d74c714); // Magic Header
this.writeUint16(output, cmdId);       // 2 bytes Opcode
this.writeUint16(output, 0);           // 2 bytes Head Size
this.writeUint32(output, protoSize);   // 4 bytes Body Size
if (this.data != null) {
    this.data.writeTo(output);         // Serialized Protobuf
}
this.writeUint32(output, 0xd7a152c8); // Magic Tail

2.3 กลไก KCP Transport และ GameSession State Machine

KCP ได้รับการปรับแต่งค่าผ่าน ChannelConfig ใน GameServer.start():

  • nodelay(true, 40, 2, true): โหมด nodelay เต็มรูปแบบ, Interval 40ms, Resend 2, ปิด Congestion Control
  • MTU: 1400 ไบต์
  • Sndwnd / Rcvwnd: 256 / 256 (บัฟเฟอร์การส่งและรับ)
  • Timeout: 30 วินาที
  • UseConvChannel: เปิดใช้งานรหัส Connection Conversation
  • AckNoDelay: เปิดใช้งาน ACK ทันที

State Machine ของ GameSession:

stateDiagram-v2
    [*] --> WAITING_FOR_TOKEN: KCP Connection Established
    WAITING_FOR_TOKEN --> WAITING_FOR_LOGIN: Recv PlayerGetTokenCsReq (Auth OK)
    WAITING_FOR_LOGIN --> ACTIVE: Recv PlayerLoginCsReq
    ACTIVE --> ACTIVE: Normal Game Packets & Heartbeat
    ACTIVE --> INACTIVE: Disconnect / Timeout / Kicked
    WAITING_FOR_TOKEN --> INACTIVE: Bad Token / Full Server
    INACTIVE --> [*]: Cleanup & Save to DB
  1. WAITING_FOR_TOKEN: รับได้เฉพาะ PlayerHeartBeatCsReq และ PlayerGetTokenCsReq เท่านั้น หากส่งแพ็กเก็ตอื่นจะถูกทิ้งทันที
  2. WAITING_FOR_LOGIN: ตรวจสอบบัญชีเสร็จแล้ว รอรับ PlayerLoginCsReq
  3. ACTIVE: เซสชันพร้อมทำงานเต็มรูปแบบ สามารถประมวลผลคำสั่งในเกมได้ทั้งหมด
  4. INACTIVE: เซสชันถูกตัด หลุดจากเซิร์ฟเวอร์ จะบันทึกสถานะผู้เล่นลงฐานข้อมูลทันที

3. วงจรชีวิตของผู้เล่นและการเชื่อมต่อ (Player Lifecycle & Handshake Flow)

ลำดับการแลกเปลี่ยนแพ็กเก็ต (Packet Handshake) ขณะล็อกอินเข้าสู่ตัวเกม:

sequenceDiagram
    autonumber
    actor Client as Game Client
    participant KCP as GameServer (KCP)
    participant Session as GameSession
    participant DB as MongoDB

    Client->>KCP: KCP Connect Handshake
    KCP->>Session: Create Session (State = WAITING_FOR_TOKEN)
    
    Client->>Session: PlayerGetTokenCsReq (AccountUid, Token)
    Session->>DB: Query Account by Uid & Validate Token
    Session->>DB: Query Player (or create new if null)
    Session->>Session: player.onLogin() (Load Avatars, Inventory, Lineup)
    Session->>Session: State = WAITING_FOR_LOGIN
    Session-->>Client: PlayerGetTokenScRsp (Uid, BlackInfo)

    Client->>Session: PlayerLoginCsReq
    Session->>Session: State = ACTIVE
    Session-->>Client: PlayerLoginScRsp (BasicInfo, ServerTime, Stamina)
    Session-->>Client: StaminaInfoScNotify

    Client->>Session: PlayerLoginFinishCsReq
    Session-->>Client: PlayerLoginFinishScRsp
    Session-->>Client: GetArchiveDataScRsp
    Session-->>Client: BattlePassInfoNotify
    Session-->>Client: ContentPackageGetDataScRsp

    Client->>Session: GetCurSceneInfoCsReq
    Session-->>Client: GetCurSceneInfoScRsp (SceneInfo, Entities, Lineup)

    Client->>Session: EnterSceneCsReq (Teleport / Map Loaded)
    Session-->>Client: EnterSceneScRsp
    Session-->>Client: SceneEntityMoveScNotify (Spawn Avatar in Scene)

4. โครงสร้างข้อมูลและฐานข้อมูล (Persistence & Morphia ODM)

4.1 Internal MongoDB vs Standalone MongoDB

LunarCore รองรับสองโหมดการทำงานของฐานข้อมูลผ่าน Config.DatabaseInfo:

  • โหมดภายนอก (External MongoDB): ต่อไปยัง MongoDB Server ปกติ (เช่น mongodb://localhost:27017)
  • โหมดภายใน (Internal MongoDB Server): เริ่มต้นเซิร์ฟเวอร์จำลองด้วย de.bwaldvogel.mongo.MongoServer โดยเลือก Backend ได้ 2 รูปแบบ:
    1. H2Backend: บันทึกลงดิสก์เป็นไฟล์ database.mv (Persist ถาวร)
    2. MemoryBackend: รันใน RAM เท่านั้น (ข้อมูลหายเมื่อปิดเซิร์ฟเวอร์)

4.2 การแบ่งแยก Collections และ Document Schema

Collection Entity Class Primary Key Key Indexes หน้าที่และรายละเอียดฟิลด์สำคัญ
accounts Account String _id token, sessionKey จัดเก็บข้อมูลล็อกอิน, comboToken, permissions (Set<String>)
players Player int _id (uid) accountUid ข้อมูลหลัก: ชื่อ, เลเวล, ค่าเงิน (scoin, hcoin, mcoin), Stamina, พิกัดตำแหน่งฉากล่าสุด (planeId, floorId, pos, rot)
avatars GameAvatar ObjectId _id ownerUid ตัวละครที่ครอบครอง: avatarId, level, promotion, rank (Eidolon), skills (Tree points), equips
items GameItem ObjectId _id ownerUid, equipAvatarId ไอเทมทุกประเภท: Relics (Main/Sub affixes), Light Cones (level, rank), Materials, Currencies
lineups PlayerLineup ObjectId _id ownerUid จัดทีม 4 ตำแหน่ง, ผู้นำทีม (leader), สกิลพอยต์ (mp)
extra_lineups PlayerExtraLineup ObjectId _id ownerUid ทีมพิเศษสำหรับ Memory of Chaos, Simulated Universe, Trial
challenges ChallengeHistory ObjectId _id ownerUid บันทึกสถิติด่าน Forgotten Hall / Pure Fiction (ดาวที่ได้, รอบที่ใช้)
mail Mail ObjectId _id ownerUid จดหมายในเกม: หัวข้อ, เนื้อหา, วันหมดอายุ, ของแนบ (attachments)
roguetalents RogueTalentData ObjectId _id ownerUid อัปเกรดแผนผังพรสวรรค์ของ Simulated Universe

Important

การแยกความสัมพันธ์แบบ Loose Coupling ใน Inventory & Avatar:
ฟิลด์ equips ในคลาส AvatarData ถูกตั้งค่าเป็น transient โดยที่ตัวเก็บข้อมูลจริงคือ GameItem ที่มีฟิลด์ @Indexed ObjectId equipAvatarId เมื่อโหลดเกม ระบบจะ query ไอเทมทั้งหมดในคอลเลกชัน items ที่มี ownerUid ตรงกัน แล้วเชื่อมโยงเข้ากับอ็อบเจกต์ตัวละครในหน่วยความจำ วิธีนี้ป้องกันปัญหา Data Desync ระหว่างช่องเก็บของและตัวละครอย่างสมบูรณ์


5. ระบบจัดการทรัพยากรเกมและตารางข้อมูล (Game Data & Resource Pipeline)

ข้อมูลเกมทั้งหมด (Game Data) อ่านมาจากไฟล์ JSON ของเกม ซึ่งดึงมาจากคลังทรัพยากร เช่น StarRailData ของ Dimbreath:

  • ไดเรกทอรี: resources/ExcelOutput/
  • ไดเรกทอรีแผนที่: resources/Config/LevelOutput/RuntimeFloor/

วงจรการโหลดข้อมูล (ResourceLoader.java):

  1. Reflection Scan: สแกนหาทุกคลาสที่สืบทอดมาจาก GameResource และมี Annotation @ResourceType
  2. Priority Ordering: จัดลำดับการโหลดตาม loadPriority เพื่อให้ข้อมูลพื้นฐาน (เช่น TextMap, Rarity, Stats) โหลดเสร็จก่อนข้อมูลที่ขึ้นต่อกัน (เช่น Avatars, Relics, Stages)
  3. Custom Deserializers: ใช้ Gson ร่วมกับ TypeAdapter เฉพาะ:
    • LunarCoreDoubleDeserializer: ป้องกันปัญหาความแม่นยำของเลข Floating-point
    • LunarCoreHashDeserializer: แปลง Hash String ใน ExcelBin เป็น Integer/Long Key
  4. Depot Caching (GameDepot.java): สร้างแคชสำหรับค้นหาอย่างรวดเร็ว เช่น:
    • ตารางน้ำหนักการสุ่ม Main Affix ของ Relic แยกตามชิ้นส่วน
    • ตารางการสุ่ม Sub Affix และอัตราการเติบโตเมื่ออัปเกรด
    • ตาราง Drop Group และ Monster Wave Templates

6. ระบบฉากและ Entity ในโลกเปิด (Scene & Entity Engine)

ระบบฉากของ LunarCore ถูกควบคุมด้วยคลาส Scene.java โดยอิงตามแนวคิดของเกม:

  • Plane (MazePlaneExcel): ขอบเขตของแผนที่หลัก (เช่น Space Station, Jarilo-VI, Xianzhou Luofu, Penacony)
  • Floor (FloorInfo): ชั้นย่อยในฉากนั้นๆ (โหลดมาจาก LevelOutput/RuntimeFloor/*.json)
  • Groups (GroupInfo): กลุ่มของ Entity ที่อยู่ในฉาก (จุดเกิดของมอนสเตอร์, หีบสมบัติ, เสาฟื้นฟูเลือด, ประตูมิติ)
classDiagram
    class Scene {
        -Player player
        -int planeId
        -int floorId
        -Int2ObjectMap~GameEntity~ entities
        -IntSet avatarEntityIds
        +loadGroup(groupId)
        +addEntity(entity)
        +removeEntity(entity)
        +onTick()
    }

    class GameEntity {
        <<interface>>
        +getEntityId() int
        +getPos() Position
        +getRot() Position
        +toProto() SceneEntityInfo
    }

    class GameAvatar {
        -int avatarId
        -int currentHp
        -int currentSp
    }

    class EntityMonster {
        -int monsterId
        -int stageId
        -int worldLevel
        -boolean isAlive
    }

    class EntityProp {
        -int propId
        -PropType propType
        -PropState propState
    }

    class EntityNpc {
        -int npcId
        -int talkId
    }

    GameEntity <|.. GameAvatar
    GameEntity <|.. EntityMonster
    GameEntity <|.. EntityProp
    GameEntity <|.. EntityNpc
    Scene o-- GameEntity

กลไก Entity ID และ World Sync:

  • Entity แต่ละตัวในฉากจะได้ entityId แบบ Dynamic ซึ่งเริ่มต้นรันเลขเพิ่มขึ้นเรื่อยๆ ต่อฉาก
  • ผู้เล่นส่งพิกัดการเคลื่อนที่ผ่านแพ็กเก็ต SceneEntityMoveCsReq
  • เซิร์ฟเวอร์อัปเดตพิกัด pos และ rot ของ Player ในหน่วยความจำ และกระจายแพ็กเก็ต SceneEntityMoveScNotify
  • ระบบ Space Anchor (เสาฮีล): ค้นหาเสาที่ใกล้ที่สุดในฉากผ่าน Scene.getNearestSpring() เพื่อใช้เป็นจุดฟื้นฟู HP/MP หรือจุดวาปกลับเมื่อพ่ายแพ้ในการต่อสู้

7. ระบบการต่อสู้ (Battle Orchestration & Reconciliation)

จุดเด่นของสถาปัตยกรรม Honkai: Star Rail คือ การจำลองลูปการต่อสู้เทิร์นเบสเกิดขึ้นที่ Client-side ทั้งหมด โดยเซิร์ฟเวอร์ทำหน้าที่เป็น Authoritative Orchestrator ในการเตรียมข้อมูลด่าน และตรวจสอบผลลัพธ์หลังสู้เสร็จ

sequenceDiagram
    autonumber
    actor Client as Game Client
    participant BattleSvc as BattleService
    participant Battle as Battle Instance
    participant DropSvc as DropService

    Note over Client,BattleSvc: 1. การเปิดฉากต่อสู้ในโลกเปิด
    Client->>BattleSvc: SceneCastSkillCsReq (ใช้วิชา Technique หรือโจมตีก่อน)
    alt เป็นมอนสเตอร์โจมตีผู้เล่น
        BattleSvc->>Battle: สร้าง Battle พร้อม Ambush Buff (1000102)
    else เป็นผู้เล่นโจมตีก่อน
        BattleSvc->>Battle: สร้าง Battle พร้อม Element Weakness Debuff ของตัวละคร
    end
    
    BattleSvc->>Battle: รวบรวม Monster Waves จาก StageExcel
    BattleSvc->>Battle: รวบรวมค่าสถานะ HP/SP/Buffs ปัจจุบันของทีม
    BattleSvc-->>Client: SceneCastSkillScRsp (SceneBattleInfo Proto)

    Note over Client: 2. ไคลเอนต์จำลองการต่อสู้แบบเทิร์นเบสในตัวเครื่อง

    Note over Client,BattleSvc: 3. การรายงานผลลัพธ์การต่อสู้
    Client->>BattleSvc: PVEBattleResultCsReq (EndStatus, BattleStatistics)
    
    alt ผู้เล่นชนะ (BATTLE_END_WIN)
        BattleSvc->>DropSvc: คำนวณรางวัลและของดรอป (Drops)
        BattleSvc->>BattleSvc: หักค่าพลังกาย Stamina (ถ้ามี)
        BattleSvc->>Client: ลบมอนสเตอร์ตัวนั้นออกจาก Scene
    else ผู้เล่นแพ้ (BATTLE_END_LOSE)
        BattleSvc->>BattleSvc: รีเซ็ต HP ตัวละครเหลือ 20%
        BattleSvc->>Client: วาปตัวละครกลับไปเสา Space Anchor ที่ใกล้ที่สุด
    else ผู้เล่นกดยอมแพ้ (BATTLE_END_QUIT)
        BattleSvc->>Client: คืนค่าสถานะเดิมและวาปกลับเสา
    end

    BattleSvc->>BattleSvc: อัปเดต HP และ SP สุทธิของตัวละครลง DB
    BattleSvc-->>Client: PVEBattleResultScRsp (รางวัลที่ได้รับ, EXP)

รายละเอียดโครงสร้าง SceneBattleInfo:

  • battleId: เลขรันประจำการต่อสู้ของผู้เล่น
  • stageId: ID ด่านจาก StageExcel
  • logicRandomSeed: เลขสุ่ม Seed สำหรับให้ Client ใช้คำนวณ Random Roll ในเทิร์น
  • monsterWaveList: รายการ Wave ของมอนสเตอร์ (Level, Monster ID, Elite Affixes)
  • battleAvatarList: ข้อมูลตัวละคร 4 ตัวในทีม (Level, Promotion, Relics, Light Cone, ค่า HP/SP ปัจจุบัน)
  • buffList: บัฟที่ติดมาจากโลกเปิด เช่น ยาบัฟอาหาร, วิชา Technique ก่อนเข้าสู้
  • battleTargetInfo: เงื่อนไข 3 ดาว สำหรับด่าน Forgotten Hall / Pure Fiction

8. ระบบโหมดการเล่นขั้นสูง (Simulated Universe & Challenge Modes)

8.1 Simulated Universe (Rogue Engine)

ระบบจำลองจักรวาล (Simulated Universe) ถูกแยกการทำงานไว้ในแพ็กเกจ emu.lunarcore.game.rogue:

  • RogueInstance: ตัวแทนของรอบการเล่นปัจจุบัน (Run State)
    • เก็บค่า RogueRoomData: ห้องต่อสู้, ห้อง Elite, ห้องเหตุการณ์ (Event), ห้องพัก (Respite), ห้องบอส
    • จัดการสกิลพอยต์จักรวาล Cosmic Fragments (Item ID: 31)
  • ระบบเลือกพร (Blessings / Buffs):
    • ควบคุมผ่าน RogueBuffSelectMenu
    • เมื่อชนะห้องต่อสู้ เซิร์ฟเวอร์สุ่มพรตาม Path ที่เลือก และส่ง AddRogueBuffScNotify
  • ระบบวัตถุหายาก (Curios / Miracles):
    • ควบคุมผ่าน RogueMiracleSelectMenu ให้ผู้เล่นเลือกรับ Curio
  • ระบบเหตุการณ์โต้ตอบ (Interactive Events):
    • ควบคุมผ่าน RogueEventManager และ RogueEventInstance
    • ไคลเอนต์ส่ง SelectRogueCommonDialogueOptionCsReq เมื่อผู้เล่นเลือกตัวเลือกในเหตุการณ์ เซิร์ฟเวอร์คำนวณผลกระทบ (ได้เงิน, เสียเลือด, ได้พร) แล้วส่งผลลัพธ์กลับ

8.2 Forgotten Hall & Pure Fiction (Challenge Subsystem)

  • ควบคุมผ่าน ChallengeManager.java และ ChallengeInstance.java
  • จัดการรอบ (Rounds Limit) เช่น กำหนดรอบเหลือไม่ต่ำกว่า 20 เพื่อรับ 3 ดาว
  • โหลด Lineup แบบแยกอิสระ (ทีมครึ่งแรก และทีมครึ่งหลัง) ผ่าน PlayerExtraLineup
  • บันทึกประวัติและดาวที่ทำได้ลงคอลเลกชัน challenges

9. ระบบตัวละคร, คลังสัมภาระ และไอเทม (Avatar & Inventory Subsystem)

9.1 Multi-Path Switching (ระบบเปลี่ยนคลาส Trailblazer)

ใน Honkai: Star Rail ตัวเอก (Trailblazer) สามารถเปลี่ยนเส้นทาง Path ได้ (ทำลายล้างกายภาพ, อนุรักษ์ไฟ, ประสานจินตภาพ, ล่าสังหารน้ำแข็ง):

  • LunarCore ออกแบบผ่านคลาส AvatarMultiPath.java
  • เมื่อผู้เล่นกดเปลี่ยน Path ไคลเอนต์จะส่ง SetAvatarPathCsReq:
    1. เซิร์ฟเวอร์ดึงข้อมูลจาก MultiplePathAvatarExcel
    2. โหลด AvatarMultiPath ที่บันทึกเลเวลสกิลของ Path นั้นๆ
    3. สลับข้อมูล skills, equips, และโมเดลเพศ (gender) ใน GameAvatar
    4. บันทึกลงตาราง curAvatarPaths ในเอกสาร players
    5. ส่งแพ็กเก็ต PacketAvatarPathChangedNotify และ PacketPlayerSyncScNotify ไปยัง Client

9.2 ระบบสุ่มค่า Relic (Main Affix & Sub Affix Step Generator)

การสร้าง Relic ใน GameItem.java อิงตามหลักคณิตศาสตร์ของเกมจริง:

[Main Affix Allocation]
1. ตรวจสอบชิ้นส่วนของ Relic (Head, Hands, Body, Feet, Planar Sphere, Link Rope)
2. ดึงตาราง MainAffixGroup จาก RelicExcel
3. สุ่มค่า Stat หลักตาม Weight Distribution ใน GameDepot

[Sub Affix Allocation]
1. คำนวณจำนวน Sub Affix เริ่มต้นตาม Rarity:
   Base Count = clamp(Rarity - 2, 0, 3) + randomRange(0, 1)
   (ตัวอย่างเช่น Relic 5 ดาว จะได้ Sub Affix เริ่มต้น 3 หรือ 4 แถว)
2. สุ่มเลือก Stat รองโดยห้ามซ้ำกับ Main Affix และห้ามซ้ำกับ Sub Affix อื่นที่มีอยู่แล้ว
3. สำหรับแต่ละ Sub Affix จะสุ่มค่า Step (Roll Quality):
   Step มีค่า 0, 1, หรือ 2 (แทนค่า Small, Medium, Large Roll)

เมื่อมีการอัปเกรด Relic ทุกๆ 3 เลเวล (HandlerExpUpRelicCsReq):

  • หากจำนวน Sub Affix ยังไม่ครบ 4 แถว ระบบจะเพิ่มแถวใหม่
  • หากครบ 4 แถวแล้ว ระบบจะสุ่มเลือก 1 แถวที่มีอยู่เพื่อเพิ่ม count และสุ่มค่า step ใหม่

10. ระบบ Gacha และคำสั่งเซิร์ฟเวอร์ (Gacha, Commands & Plugins)

10.1 สถาปัตยกรรมตู้กาชา (GachaService.java)

  • การ Hot-Reload ด้วย WatchService: ตัวบริการลงทะเบียน File Watcher ตรวจจับไฟล์ data/Banners.json หากผู้ดูแลแก้ไขไฟล์ระบบจะ reload ตู้กาชาทันทีโดยไม่ต้องรีสตาร์ตเซิร์ฟเวอร์
  • การคำนวณการันตี (Pity Mechanics):
    • 5-Star Soft Pity: อัตราพื้นฐาน 0.6% เมื่อเปิดถึงโรลที่ 74 อัตราจะเพิ่มขึ้นแบบเส้นตรงจนถึง 100% ที่โรล 90
    • ระบบหลุดเรท 50/50: หากเปิดได้ 5 ดาวที่ไม่ใช่หน้าตู้ ตัวแปร isFeatured จะถูกเซ็ตเป็น true สำหรับ 5 ดาวรอบถัดไป
    • การันตี 4 ดาว: ทุกๆ 10 โรล

10.2 เครื่องมือคำสั่งเซิร์ฟเวอร์ (Command Engine)

  • ทำงานผ่านทั้งคอนโซลเซิร์ฟเวอร์ และช่องทางแชตในเกม
  • ในเกมจะมีบัญชีปลอมชื่อ "Server" (UID: 99) ปรากฏในรายชื่อเพื่อนโดยอัตโนมัติ
  • ผู้เล่นส่งข้อความที่ขึ้นต้นด้วย / หรือ ! (เช่น /give 1001, /heal, /scene 20001 20001001)
  • ChatManager จะดักจับข้อความและส่งต่อให้ CommandManager.invoke(player, message)
  • ตรวจสอบสิทธิ์ผ่านระบบ RBAC: account.hasPermission(command.permission())

10.3 ระบบปลั๊กอิน (Plugin Architecture)

  • รองรับการโหลดไฟล์ JAR จากโฟลเดอร์ plugins/
  • แต่ละปลั๊กอินมี plugin.json ระบุ Main Class, Version, Author
  • โหลดผ่าน URLClassLoader อิสระ
  • มี Lifecycle Hooks: onLoad(), onEnable(), onDisable()

11. สรุปข้อสังเกตและคู่มือการนำไปพัฒนาต่อสำหรับ AI

หากโมเดล AI ตัวอื่นต้องการนำข้อมูลสถาปัตยกรรมนี้ไปเขียนโค้ดต่อ หรือสร้างเซิร์ฟเวอร์ใหม่ในภาษาอื่น (เช่น Go, Rust, C#):

  1. Protocol Framing: ห้ามลืมกรอบ 16 ไบต์เด็ดขาด (0x9D74C714 นำหน้า และ 0xD7A152C8 ปิดท้าย) หากไบต์ขาดหรือผิดแฮนด์เชกจะค้าง
  2. Gateway Encoding: แพ็กเก็ต HTTP Gateway (/query_dispatch และ /query_gateway) ต้องแปลงเป็น Protobuf ไบนารีก่อน แล้วค่อยครอบด้วย Base64 String ส่งเป็นเนื้อหา HTTP Response
  3. Turn-based Battle Paradigm: เซิร์ฟเวอร์ไม่ต้องสร้าง AI คำนวณดาเมจต่อเทิร์น แต่ต้องเตรียม SceneBattleInfo ให้ครบถ้วน (ตัวละคร, มอนสเตอร์แต่ละเวฟ, บัฟ, ค่าพลังกาย) แล้วรอรับผลสรุป PVEBattleResultCsReq จาก Client
  4. Data Synchronization (Syncable): หลังการเปลี่ยนแปลงไอเทมหรือตัวละคร ต้องส่ง PacketPlayerSyncScNotify เสมอ เพื่อให้หน้าต่าง UI ของเกมแสดงผลตรงกับฐานข้อมูล
  5. Session States: รักษา State Flow ให้เคร่งครัดตามลำดับ WAITING_FOR_TOKEN -> WAITING_FOR_LOGIN -> ACTIVE อย่าตอบรับคำสั่งเกมก่อนเซสชันจะอยู่ในสถานะ ACTIVE