# รายงานการวิเคราะห์สถาปัตยกรรมและการทำงานของ FireflyProtoc (Deep-Dive Technical Specification) > **เอกสารอ้างอิงทางเทคนิคระดับลึกสำหรับวิศวกรซอฟต์แวร์และโมเดลปัญญาประดิษฐ์ (AI Systems)** > วิเคราะห์จากซอร์สโค้ดของโครงการ: [gatokeeper/FireflyProtoc](https://github.com/gatokeeper/FireflyProtoc) > ครอบคลุมสถาปัตยกรรมคอมไพเลอร์ Protobuf ยุคใหม่, การถอดรหัสความคลุมเครือของข้อมูล (Wire De-obfuscation), ระบบคณิตศาสตร์เชิงสัญลักษณ์ (Symbolic Expression Inverter), Meta Fields, Compile-Time Message Factory และรันไทม์ประสิทธิภาพสูง --- ## สารบัญ (Table of Contents) 1. [บทนำและวัตถุประสงค์ของโครงการ (Introduction & Problem Statement)](#1-บทนำและวัตถุประสงค์ของโครงการ-introduction--problem-statement) 2. [สถาปัตยกรรมคอมไพเลอร์แบบ Multi-Tiered IR (Compiler Architecture)](#2-สถาปัตยกรรมคอมไพเลอร์แบบ-multi-tiered-ir-compiler-architecture) - 2.1 ภาพรวมของไปป์ไลน์การคอมไพล์ (Frontend $\rightarrow$ IR $\rightarrow$ Backend) - 2.2 โครงสร้างไวยากรณ์เสริม (Custom Syntax Extensions) 3. [ระบบคณิตศาสตร์เชิงสัญลักษณ์ (Symbolic Expression Inverter Engine)](#3-ระบบคณิตศาสตร์เชิงสัญลักษณ์-symbolic-expression-inverter-engine) - 3.1 ปัญหาการพรางค่าข้อมูลบนสายส่ง (Wire Obfuscation / Masking) - 3.2 อัลกอริทึมการแปลงนิพจน์ผกผันพีชคณิตอัตโนมัติ (Algebraic Inversion) 4. [ระบบ Meta Fields และ Message Factory แบบ Zero-Reflection](#4-ระบบ-meta-fields-และ-message-factory-แบบ-zero-reflection) - 4.1 การฝัง Metadata (CmdId/Opcode) โดยไม่เสีย Overhead บนสายส่ง - 4.2 การสร้าง Compile-Time Factory Jump Table (`IFactoryMessage`) 5. [การสร้างโค้ด C# และการปรับปรุงประสิทธิภาพ (CodeGen & Performance Optimizations)](#5-การสร้างโค้ด-c-และการปรับปรุงประสิทธิภาพ-codegen--performance-optimizations) 6. [รันไทม์ภายในของ Firefly (Firefly.Protobuf Runtime Architecture)](#6-รันไทม์ภายในของ-firefly-fireflyprotobuf-runtime-architecture) 7. [ตัวอย่างจริงจากการคอมไพล์ (Real-World Compilation Walkthrough)](#7-ตัวอย่างจริงจากการคอมไพล์-real-world-compilation-walkthrough) 8. [การนำไปประยุกต์ใช้ในระบบเซิร์ฟเวอร์เกม (Integration with HSR/Genshin Private Servers)](#8-การนำไปประยุกต์ใช้ในระบบเซิร์ฟเวอร์เกม-integration-with-hsrgenshin-private-servers) 9. [คู่มือวิศวกรรมสำหรับ AI ในการขยายขีดความสามารถ (AI Extension & Engineering Guide)](#9-คู่มือวิศวกรรมสำหรับ-ai-ในการขยายขีดความสามารถ-ai-extension--engineering-guide) --- ## 1. บทนำและวัตถุประสงค์ของโครงการ (Introduction & Problem Statement) **FireflyProtoc** (พัฒนาโดย `gatokeeper`) คือคอมไพเลอร์ Protocol Buffers ทางเลือกที่ถูกออกแบบขึ้นมาโดยเฉพาะสำหรับ **Protocol วิศวกรรมย้อนกลับ (Reverse Engineering)** และ **เซิร์ฟเวอร์จำลองเกม (Game Emulators)** เช่น *Honkai: Star Rail*, *Genshin Impact*, และ *Zenless Zone Zero* ### ข้อจำกัดของ `protoc` ดั้งเดิมของ Google: 1. **ไม่รองรับ Wire Data Masking / Obfuscation**: เกมค่าย HoYoverse มักจะพรางค่าฟิลด์ตัวเลขบนสายส่ง (Wire Format) ด้วยการทำ XOR, บวก, ลบ หรือบิตชิฟต์ เช่น ค่า ID จริงคือ `1001` แต่ในสายส่งกลับถูกเข้ารหัสเป็น `(1001 ^ 0x5A) + 7` คอมไพเลอร์ปกติของ Google ทำได้แค่อ่านค่าดิบ ทำให้วิศวกรต้องมาเขียนโค้ดแปลงค่าซ้ำซ้อนในทุก ๆ ฟิลด์ 2. **ไม่มีแนวคิดเรื่อง Packet Opcode (CmdId) ในตัว**: Protobuf มาตรฐานไม่มีช่องทางในการผูกหมายเลข Opcode เข้ากับ Class โดยตรง หากสร้างฟิลด์ธรรมดา ข้อมูล Opcode จะถูกส่งซ้ำซ้อนลงไปใน Payload สิ้นเปลืองแบนด์วิดท์ 3. **การสร้างอินสแตนซ์พึ่งพา Reflection หรือ Dictionary**: การแมป Opcode เป็น C# Object มักต้องใช้ Reflection หรือ Dictionary Lookup ขนาดใหญ่ ซึ่งช้าและไม่รองรับการทำ NativeAOT FireflyProtoc แก้ปัญหาทั้งหมดนี้ที่ **ระดับคอมไพเลอร์ (Compiler Level)** ทำให้ไฟล์ `.proto` มีความสามารถในการแปลงค่าพรางข้อมูลอัตโนมัติและสร้าง O(1) Factory สำเร็จรูปทันที --- ## 2. สถาปัตยกรรมคอมไพเลอร์แบบ Multi-Tiered IR (Compiler Architecture) FireflyProtoc ถูกสร้างขึ้นด้วยสถาปัตยกรรมคอมไพเลอร์ระดับมืออาชีพ โดยแบ่งขั้นตอนการทำงานออกเป็น **3 ชั้น Intermediate Representation (IR)**: ```mermaid flowchart TD ProtoFile[".proto Source Code
(พร้อมไวยากรณ์เสริม meta, [get: ...])"] --> Lexer["Lexer (Token Stream)"] Lexer --> Parser["Parser (AST: Node / Decl / Expr)"] Parser --> TypeChecker["TypeChecker (Semantic Validation)"] subgraph IR_Pipeline["Multi-Tiered IR Lowering"] TypeChecker --> HIR["HIR Builder
(High-Level IR: Messages, Enums, Scopes)"] HIR --> MIR["MIR Builder
(Mid-Level IR: Type Resolution, MessageFactory, PascalCase)"] MIR --> LIR["LIR Builder
(Low-Level IR: Classes, Methods, Body, Byte Stmts)"] end LIR --> CodeGen["CSharpGenerator (Parallel.ForEach File Emit)"] CodeGen --> CSharpFiles["Generated High-Performance C# Files
(Zero Reflection, AggressiveInlining, NativeAOT)"] ``` ### 2.1 หน้าที่ของแต่ละเลเยอร์ในไปป์ไลน์: 1. **AST (Abstract Syntax Tree)**: - แปลงไวยากรณ์ข้อความเป็นโครงสร้างข้อมูลเบื้องต้น (`Decl.Message`, `Decl.Field`, `Decl.Enum`, `Expr.Binary`) 2. **HIR (High-Level Intermediate Representation)**: - รวบรวม Scope และ Namespace ของไฟล์ - จัดระเบียบความสัมพันธ์ของ Message ซ้อน Message (Nested Types) - สกัดนิพจน์ `[get: ...]` และ `[set: ...]` เข้าสู่ `AttributeSet` 3. **MIR (Mid-Level Intermediate Representation)**: - แมปประเภทข้อมูลของ Protobuf ไปเป็นประเภทข้อมูล C# (`MirTypeRef`) - แปลงชื่อแบบ snake_case เป็น PascalCase - รวบรวมฟิลด์ที่เป็น `meta` ที่ตรงกับคำสั่ง `option message_factory` เพื่อเตรียมสร้าง Factory Class 4. **LIR (Low-Level Intermediate Representation)**: - กำหนดโครงสร้างโค้ดระดับล่าง: Constructors, Property Getters/Setters, ฟิลด์ Backing Field (`_Field`), เมธอด `WriteTo`, `MergeFrom`, `CalculateSize`, `Clone`, และ Switch-Case ใน `MessageFactory.Create` 5. **CodeGen (C# Code Generator)**: - แปลง LIR ออกมาเป็นโค้ด C# ข้อความจริง โดยรันกระบวนการเขียนไฟล์แบบคู่ขนาน (`Parallel.ForEach`) ### 2.2 โครงสร้างไวยากรณ์เสริม (Custom Syntax Extensions) | ไวยากรณ์ | รูปแบบการเขียน | ผลลัพธ์ที่คอมไพเลอร์สร้างให้ | |---|---|---| | **Meta Field** | `meta int32 cmd_id = 1;` | สร้างค่าคงที่และ Getter ในคลาส **แต่จะไม่ถูก Serialize ออกทางสายส่ง** และสามารถใช้ Tag ชนกับฟิลด์ธรรมดาได้ | | **Masked Field** | `[get: (id ^ 1) + 2] int32 id = 3;` | ตัวแปร `Id` ภายนอกจะมองเห็นค่าจริง แต่ค่าที่ถูกส่ง/รับบนสายส่งจะผ่านสูตรคณิตศาสตร์ผกผัน | | **Message Factory** | `option message_factory = int32 cmd_id;` | สร้างคลาส `MessageFactory` พร้อมเมธอด `Create(int cmd_id)` คืนค่าอินสแตนซ์ของ Message นั้นทันที | --- ## 3. ระบบคณิตศาสตร์เชิงสัญลักษณ์ (Symbolic Expression Inverter Engine) จุดที่อัจฉริยะที่สุดของ FireflyProtoc คือคลาส **`ExpressionInverter.cs`** ซึ่งทำหน้าที่เป็น Symbolic Algebra Solver ในตัวเอง ### 3.1 ปัญหาการพรางค่าข้อมูลบนสายส่ง (Wire Obfuscation / Masking) สมมุติว่าในเกม ฟิลด์ `hp` มีสูตรการพรางข้อมูลทางสายส่งคือ: $$\text{WireValue} = (\text{LogicalHP} \oplus 1) + 2$$ เมื่อเขียนโค้ดฝั่งอ่าน (Deserialization / `MergeFrom`), เราจำเป็นต้องถอดรหัสค่าจากสายส่งกลับมาเป็นค่าจริง: $$\text{LogicalHP} = (\text{WireValue} - 2) \oplus 1$$ หากใช้คอมไพเลอร์ทั่วไป ผู้พัฒนาจะต้องมานั่งคำนวณและเขียนสูตรทั้งขาไป (`get`) และขากลับ (`set`) ด้วยตัวเอง หากมีฟิลด์แบบนี้เป็นร้อยๆ ฟิลด์ จะเกิดข้อผิดพลาดได้ง่ายมาก ### 3.2 อัลกอริทึมการแปลงนิพจน์ผกผันพีชคณิตอัตโนมัติ (Algebraic Inversion) ใน FireflyProtoc ผู้ใช้สามารถเขียนเพียงขาเดียว: ```protobuf [get: (id ^ 1) + 2] int32 id = 3; ``` `ExpressionInverter` จะทำการคลายโหนด (Unwind) ย้อนกลับจาก Root ของ AST Expression Tree: ```mermaid flowchart TD Input["Input: (id ^ 1) + 2"] --> Root["Root Node: Operator (+) with Constant (2)"] Root --> InvertAdd["Invert (+) to (-)
New Accumulator: (WireValue - 2)"] InvertAdd --> NextNode["Next Node: Operator (^) with Constant (1)"] NextNode --> InvertXor["Invert (^) to (^)
Self-inverse Operator
New Accumulator: ((WireValue - 2) ^ 1)"] InvertXor --> ReachTarget["Reached Target Identifier 'id'"] ReachTarget --> Output["Setter Expression: value - 2 ^ 1"] ``` #### ตารางการสลับตัวดำเนินการพีชคณิต (`ExpressionInverter.cs`): - บวก ($+$) $\Longleftrightarrow$ ลบ ($-$) - คูณ ($*$) $\Longleftrightarrow$ หาร ($/$) - บิตไวส์ XOR ($\oplus$) $\Longleftrightarrow$ บิตไวส์ XOR ($\oplus$) *(XOR เป็น Self-Inverse Operator: $A \oplus B \oplus B = A$)* - ลบเอกภาพ (Unary $-$) $\Longleftrightarrow$ ลบเอกภาพ (Unary $-$) --- ## 4. ระบบ Meta Fields และ Message Factory แบบ Zero-Reflection ### 4.1 การฝัง Metadata (CmdId/Opcode) โดยไม่เสีย Overhead บนสายส่ง ในไฟล์ `.proto` สามารถกำหนดฟิลด์เป็น `meta`: ```protobuf message PlayerLoginCsReq { meta int32 cmd_id = 11; string token = 1; } ``` เมื่อคอมไพล์: - `cmd_id` จะไม่ถูกนับรวมใน `CalculateSize()` - `cmd_id` จะไม่ถูกเขียนลง `WriteTo(CodedOutputStream output)` - `cmd_id` จะไม่ถูกอ่านใน `MergeFrom(CodedInputStream input)` - คลาสจะมีเมธอด: ```csharp public const int CmdId = 11; public int GetCmdId() => CmdId; ``` ### 4.2 การสร้าง Compile-Time Factory Jump Table (`IFactoryMessage`) เมื่อกำหนด `option message_factory = int32 cmd_id;` ที่หัวไฟล์ `.proto` คอมไพเลอร์จะรวบรวมทุก Message ที่มีฟิลด์ `meta int32 cmd_id = X;` แล้วสร้างไฟล์ `MessageFactory.cs`: ```csharp public static class MessageFactory { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IFactoryMessage Create(int cmdId) { switch (cmdId) { case 11: return new PlayerLoginCsReq(); case 12: return new PlayerLoginScRsp(); default: return null; } } } ``` > [!TIP] > **ความได้เปรียบระดับสูงด้านประสิทธิภาพ:** > การสร้าง Switch-Case สำหรับ Type Instantiation ในขั้นตอน Compile-Time ทำให้คอมไพลเลอร์ C# และ JIT/AOT สามารถปรับให้เป็น **Jump Table (O(1) Direct Branching)** ในระดับ Assembly CPU Instruction ได้โดยตรง ปราศจากการทำ Boxing/Unboxing, แฮชแมป หรือ Reflection ใดๆ ทั้งสิ้น --- ## 5. การสร้างโค้ด C# และการปรับปรุงประสิทธิภาพ (CodeGen & Performance Optimizations) โค้ด C# ที่ FireflyProtoc สร้างขึ้นมานั้น มีการใส่ Attribute พิเศษเพื่อรีดประสิทธิภาพฮาร์ดแวร์สูงสุด: 1. **`[MethodImpl(MethodImplOptions.AggressiveInlining)]`**: - บังคับให้ JIT และ NativeAOT นำโค้ดของเมธอด (เช่น Getter/Setter, Serialization routines) ไปวางแทนที่จุดเรียกโดยตรง (Inline Expansion) เพื่อตัดค่าใช้จ่ายในการ Call Stack ของ CPU 2. **`[DebuggerNonUserCode]`**: - ป้องกันไม่ให้ Debugger กระโดดเข้าไปในโค้ดที่สร้างขึ้นโดยอัตโนมัติ เพิ่มความสะดวกในการดีบักระบบของผู้พัฒนา 3. **Partial Class Architecture (`public partial class`)**: - คลาสที่สร้างขึ้นทั้งหมดเป็น `partial class` ทำให้ผู้พัฒนาสามารถเขียนโค้ดส่วนขยาย (Extension Logic, Custom Operators, Helper Methods) แยกไฟล์ได้ โดยที่โค้ดจะไม่สูญหายเมื่อคอมไพล์ `.proto` ใหม่ 4. **Zero-Allocation Stack Buffer ใน Varint Encoding**: - เมธอดสำหรับเขียนตัวเลข Varint ใช้ `Span buffer = stackalloc byte[10];` บน Stack Memory ล้วนๆ ไม่มีการจอง Heap ใดๆ --- ## 6. รันไทม์ภายในของ Firefly (Firefly.Protobuf Runtime Architecture) FireflyProtoc ไม่ได้พึ่งพาไลบรารี `Google.Protobuf.dll` แต่มาพร้อมรันไทม์ขนาดเล็กของตัวเองชื่อ **`Firefly.Protobuf`** ซึ่งอยู่ในโฟลเดอร์ `runtime/`: ```mermaid classDiagram class IMessage { <> +CalculateSize() int +MergeFrom(CodedInputStream input) +WriteTo(CodedOutputStream output) +ToByteArray() byte[] } class IFactoryMessage { <> } class CodedInputStream { -byte[] m_Buffer -int m_Position +ReadTag() int +ReadString() string +ReadInt32() int +ReadUInt32() uint +ReadBytes() ByteString } class CodedOutputStream { -Stream m_Stream +WriteRawTag(int tag) +WriteString(string value) +WriteInt32(int value) +WriteUInt32(uint value) } class MessageParser~T~ { +ParseFrom(byte[] data) T +ParseFrom(CodedInputStream input) T } IMessage <|.. IFactoryMessage ``` ### รายละเอียดส่วนประกอบหลักของรันไทม์: - **`CodedInputStream` / `CodedOutputStream`**: ควบคุมการอ่านและเขียนข้อมูลระดับบิตและไบต์ตามมาตรฐาน Wire Type (Varint, Fixed32, Fixed64, Length-Delimited) - **`ByteString`**: ตัวแทนของ Immutable Byte Array สำหรับฟิลด์ประเภท `bytes` - **`RepeatedField`**: คอลเลกชันสำหรับฟิลด์ซ้ำ (`repeated`) ที่ปรับแต่งให้ขยายบัฟเฟอร์อย่างมีประสิทธิภาพ - **`MapField`**: โครงสร้างข้อมูล Dictionary ประสิทธิภาพสูงสำหรับฟิลด์ประเภท `map` --- ## 7. ตัวอย่างจริงจากการคอมไพล์ (Real-World Compilation Walkthrough) ### 7.1 โค้ดต้นฉบับ Protobuf (`example.proto`): ```protobuf syntax = "proto3"; option csharp_namespace = "FireflyProtoc.Example"; option message_factory = int32 department_id; message Person { meta int32 department_id = 1; string name = 1; string email = 2; [get: (id ^ 1) + 2] int32 id = 3; } ``` ### 7.2 โค้ด C# ที่คอมไพล์ได้จริง (`Person.cs`): ```csharp // Generated by FireflyProtoc! do not modify! #pragma warning disable CS8618 namespace FireflyProtoc.Example; [global::System.Diagnostics.DebuggerNonUserCode] public partial class Person : global::Firefly.Protobuf.IMessage, global::Firefly.Protobuf.IFactoryMessage { public const global::System.Int32 DepartmentId = 1; public const global::System.Int32 NameFieldNumber = 1; public const global::System.Int32 EmailFieldNumber = 2; public const global::System.Int32 IdFieldNumber = 3; private global::System.Int32 _Id; [global::System.Diagnostics.DebuggerNonUserCode] public global::System.Int32 Id { [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] get { return (_Id ^ 1) + 2; // คำนวณค่าเพื่อส่งออก } [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] set { _Id = value - 2 ^ 1; // คำนวณค่าย้อนกลับอัตโนมัติจากการ Invert นิพจน์! } } [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public global::System.Int32 GetDepartmentId() { return DepartmentId; // Meta Field คืนค่าได้ แต่ไม่ถูกเขียนลง Wire } public void WriteTo(global::Firefly.Protobuf.CodedOutputStream output) { if (Name != default) { output.WriteRawTag(10); output.WriteString(_Name); } if (Email != default) { output.WriteRawTag(18); output.WriteString(_Email); } if (Id != default) { output.WriteRawTag(24); output.WriteInt32(_Id); // ส่งค่าดิบที่ผ่านการพรางแล้ว } } } ``` --- ## 8. การนำไปประยุกต์ใช้ในระบบเซิร์ฟเวอร์เกม (Integration with HSR/Genshin Private Servers) ในสถาปัตยกรรมเซิร์ฟเวอร์ เช่น **GaC-hkrpg-go** หรือ **C# NativeAOT Reference Server**: ```mermaid sequenceDiagram autonumber actor Client as Game Client (Obfuscated Traffic) participant KCP as Server KCP Layer participant Factory as MessageFactory.Create(cmdId) participant Proto as Generated IFactoryMessage Client->>KCP: UDP Packet (CmdId: 11, Obfuscated Payload) KCP->>Factory: MessageFactory.Create(11) [O(1) Switch Jump] Factory-->>KCP: Return new PlayerGetTokenCsReq() KCP->>Proto: message.MergeFrom(stream) Note over Proto: ฟิลด์ที่พรางค่าจะถูกคำนวณย้อนกลับเป็นค่าจริงอัตโนมัติใน Setter! KCP->>ServerHandler: Handler.Handle(session, message) Note over ServerHandler: Server ใช้งานฟิลด์ได้ตามปกติโดยไม่ต้องรู้สูตรพรางข้อมูล! ``` 1. **ขจัด Boilerplate Code**: เซิร์ฟเวอร์ไม่จำเป็นต้องมีโค้ดแปลงค่า (Converter Functions) นับร้อยจุด เพราะการพรางและถอดรหัสถูกฝังอยู่ในตัว Class ของ Protobuf โดยตรง 2. **ความเข้ากันได้กับโปรโตคอลหลายเวอร์ชัน**: สามารถกำหนดสูตรการพรางค่าที่แตกต่างกันในแต่ละแพตช์ได้โดยเพียงแค่แก้สัญลักษณ์ในไฟล์ `.proto` 3. **การทำงานร่วมกับระบบ Router**: สามารถเชื่อมโยง Opcode Dispatcher เข้ากับ `MessageFactory.Create(cmdId)` ได้ทันที --- ## 9. คู่มือวิศวกรรมสำหรับ AI ในการขยายขีดความสามารถ (AI Extension & Engineering Guide) หากโมเดล AI อื่นต้องการปรับแต่งหรือขยายขีดความสามารถของ FireflyProtoc ต่อไป: 1. **ข้อควรระวังเรื่อง CLI Output Path**: ใน `src/FireflyProtoc.cs` บรรทัดที่ 21 มีการฮาร์ดโค้ด `$"./{outputFolder}"` หากส่ง Path แบบ Absolute (เช่น `C:\Users\...`) จะทำให้ระบบพาธขัดข้อง (`./C:\...`) **คำแนะนำในการแก้:** เปลี่ยนให้ใช้ `Path.GetFullPath(outputFolder)` หรือตรวจสอบ `Path.IsPathRooted(outputFolder)` ก่อนต่อสตริง 2. **การคอมไพล์ในสภาพแวดล้อม .NET 10**: ไฟล์ `FireflyProtoc.csproj` มีการเปิดแฟล็ก `true` สำหรับ `net9.0` ซึ่งหากรันคำสั่ง `dotnet run` ภายใต้ .NET 10 SDK จะเกิดข้อผิดพลาดในการหา ILCompiler Targets **คำแนะนำในการรัน:** ให้ส่งแฟล็ก `-c Release -p:PublishAot=false` ขณะทดสอบ หรือปรับ `` ให้ตรงกับเวอร์ชันของ SDK ที่ติดตั้ง 3. **การเพิ่มตัวดำเนินการพรางข้อมูล (Bitshift, Modulo)**: หากต้องการให้รองรับการเลื่อนบิต (`<<`, `>>`) หรือเศษเหลือ (`%`) ให้ไปเพิ่ม Mapping ผกผันใน `InvertWithRight` และ `InvertWithLeft` ของ `src/Semantic/ExpressionInverter.cs`: - `<<` ผกผันด้วย `>>` - `>>` ผกผันด้วย `<<`