# ĐẶC TẢ GIAO THỨC NHỊ PHÂN LIÊN TIẾN TRÌNH (BINARY IPC PROTOCOL SPECIFICATION)
> **Tài liệu chuẩn hóa hợp đồng nhị phân (Binary Layout Contract) giữa Tier 1 (C++23 Core Engine) và Tier 2 (Python 3.11 Companion HUD)**  
> *Mốc thời gian tham chiếu: 08/09/2026 - Path of Exile 2 (v0.5.5 Early Access)*  
> *Đặc tả phiên bản: Protocol Version `0x00050005` (v0.5.5) - IPC v1.1*

---

## 1. TỔNG QUAN KHÔNG GIAN BỘ NHỚ CHIA SẺ (SHARED MEMORY MAP)

- **Tên đối tượng bộ nhớ dùng chung**: `Local\POE2_Auto_SharedMem_v1`
- **Kích thước ánh xạ toàn phần**: **4 MB** (`4 * 1024 * 1024 = 4,194,304 bytes` = `0x400000`).
- **Cơ chế đồng bộ hóa dữ liệu viễn trắc (Telemetry)**: **Lock-Free Seqlock Double-Buffering** (Slot 0 và Slot 1) đảm bảo 120Hz Writer không bao giờ bị block và 30Hz Reader không bao giờ gặp hiện tượng rách dữ liệu (Torn Read).
- **Cơ chế hàng đợi chỉ thị vĩ mô (Macro Commands)**: **Single-Producer Single-Consumer (SPSC) Ring Buffer 16 Slots** với mỗi slot được căn chỉnh theo 1 cache line CPU 64-byte (`alignas(64)`).

### Sơ đồ Bố cục Bộ nhớ (Memory Offset Layout)

```
0x000000 ┌────────────────────────────────────────────────────────┐
         │ SharedMemoryHeader (64 Bytes = 1 Cache Line)           │
         │ - Magic, Version, IsRunning, Seqlock, Cursors, Clocks  │
0x000040 ├────────────────────────────────────────────────────────┤
         │ Vùng đệm căn chỉnh trang (Page Alignment Padding)       │
0x001000 ├────────────────────────────────────────────────────────┤
24:          │ Telemetry Slot 0 [Ping] (Dung lượng slot: 64 KB)       │
25:          │ - Kích thước TelemetryPacket: 39,112 Bytes             │
26: 0x011000 ├────────────────────────────────────────────────────────┤
27:          │ Telemetry Slot 1 [Pong] (Dung lượng slot: 64 KB)       │
28:          │ - Kích thước TelemetryPacket: 39,112 Bytes             │
29: 0x021000 ├────────────────────────────────────────────────────────┤
30:          │ Vùng đệm mở rộng địa hình / NavMesh Cache (~2.87 MB)   │
31: 0x300000 ├────────────────────────────────────────────────────────┤
32:          │ SPSC Command Ring Buffer (16 Slots x 64 Bytes = 1024B) │
33:          │ - Gói tin CommandPacket: 36 Bytes / 64 Bytes Stride    │
34: 0x300400 ├────────────────────────────────────────────────────────┤
35:          │ Vùng nhớ dự phòng cho IPC Extensions                   │
36: 0x400000 └────────────────────────────────────────────────────────┘
37: ```
38: 
39: ---
40: 
41: ## 2. BẢNG OFFSET CHI TIẾT CÁC CẤU TRÚC DỮ LIỆU NHỊ PHÂN
42: 
43: Tất cả các cấu trúc trong giao thức đều sử dụng `#pragma pack(push, 1)` trong C++ và quy ước Little-Endian (`<`) trong module `struct` của Python.
44: 
45: ### 2.1. `SharedMemoryHeader` (Kích thước: 64 Bytes, Offset: `0x000000`)
46: 
47: Cấu trúc header quản lý đồng bộ nhịp tim và trạng thái tuần tự của Seqlock và Command Queue:
48: 
49: | Byte Offset | Độ Dài | Kiểu C++ | Kiểu Python | Tên Trường | Ý Nghĩa Kỹ Thuật |
50: | :---: | :---: | :--- | :--- | :--- | :--- |
51: | `0x00` | 8 | `uint64_t` | `Q` | `magic` | Giá trị kiểm tra ma thuật: `0x504F45324155544F` ("POE2AUTO") |
52: | `0x08` | 4 | `uint32_t` | `I` | `protocolVersion` | Phiên bản giao thức: `0x00050005` (v0.5.5) |
53: | `0x0C` | 4 | `uint32_t` | `I` | `isRunning` | Cờ trạng thái: `1` = Đang chạy, `0` = Yêu cầu Panic Stop |
54: | `0x10` | 8 | `uint64_t` | `Q` | `telemetrySequence` | **Seqlock Counter**: Số lần snapshot ghi. Slot = `(seq & 1)` |
55: | `0x18` | 8 | `uint64_t` | `Q` | `commandSequence` | **SPSC Write Cursor**: Con trỏ ghi lệnh từ Agent (Producer) |
56: | `0x20` | 8 | `uint64_t` | `Q` | `commandReadIndex` | **SPSC Read Cursor**: Con trỏ đọc lệnh của Core (Consumer) |
57: | `0x28` | 8 | `uint64_t` | `Q` | `lastCoreHeartbeat` | Timestamp Unix Epoch (ms) nhịp tim C++ Core Engine |
58: | `0x30` | 8 | `uint64_t` | `Q` | `lastAgentHeartbeat` | Timestamp Unix Epoch (ms) nhịp tim Python Companion |
59: | `0x38` | 8 | `uint64_t` | `Q` | `reserved` | Padding đệm đủ 64 bytes (1 cache line CPU) |
60: 
61: > **Quy tắc Kiểm tra Seqlock phía Python**:
62: > 1. Đọc `seq1 = read_u64(0x10)`. Nếu `seq1 & 1 != 0` (Core đang trong quá trình ghi dở), đợi và đọc lại.
63: > 2. Sao chép vùng bộ nhớ Telemetry Slot tương ứng: `slot_offset = 0x001000 if (seq1 & 1 == 0) else 0x011000`.
64: > 3. Đọc lại `seq2 = read_u64(0x10)`.
65: > 4. Nếu `seq1 == seq2`: Dữ liệu nhất quán 100%, không bị rách (Torn Read). Nếu `seq1 != seq2`, hủy bỏ và lặp lại bước 1.
66: 
67: ---
68: 
69: ### 2.2. `PlayerTelemetryData` (Kích thước: 84 Bytes)
70: 
71: Trạng thái tọa độ, hướng nhìn và các thanh sinh mệnh của người chơi (Life, Mana, Energy Shield, Spirit, Ward):
72: 
73: | Byte Offset | Độ Dài | Kiểu C++ | Kiểu Python | Tên Trường | Mô Tả |
74: | :---: | :---: | :--- | :--- | :--- | :--- |
75: | `+0x00` | 4 | `float` | `f` | `posX` | Tọa độ không gian trục X thực tế trong game |
76: | `+0x04` | 4 | `float` | `f` | `posY` | Tọa độ không gian trục Y thực tế trong game |
77: | `+0x08` | 4 | `float` | `f` | `posZ` | Tọa độ cao độ trục Z |
78: | `+0x0C` | 4 | `float` | `f` | `yaw` | Góc quay ngang (radian / độ) |
79: | `+0x10` | 4 | `float` | `f` | `pitch` | Góc ngẩng dọc |
80: | `+0x14` | 4 | `uint32_t` | `I` | `currentHP` | Máu hiện tại |
81: | `+0x18` | 4 | `uint32_t` | `I` | `maxHP` | Máu tối đa |
82: | `+0x1C` | 4 | `uint32_t` | `I` | `currentMana` | Năng lượng hiện tại |
83: | `+0x20` | 4 | `uint32_t` | `I` | `maxMana` | Năng lượng tối đa |
84: | `+0x24` | 4 | `uint32_t` | `I` | `currentES` | Khiên năng lượng (Energy Shield) hiện tại |
85: | `+0x28` | 4 | `uint32_t` | `I` | `maxES` | Khiên năng lượng tối đa |
86: | `+0x2C` | 4 | `uint32_t` | `I` | `currentSpirit` | Điểm Tinh thần (Spirit) khả dụng POE2 |
87: | `+0x30` | 4 | `uint32_t` | `I` | `maxSpirit` | Điểm Tinh thần tối đa |
88: | `+0x34` | 4 | `uint32_t` | `I` | `currentWard` | Điểm Hộ giáp (Ward) hiện tại |
89: | `+0x38` | 4 | `uint32_t` | `I` | `maxWard` | Điểm Hộ giáp (Ward) tối đa |
90: | `+0x3C` | 4 | `uint32_t` | `I` | `activeWeaponSet` | Bộ vũ khí đang kích hoạt (1 hoặc 2) |
91: | `+0x40` | 4 | `uint32_t` | `I` | `movementFlags` | Bit 0: Moving, Bit 1: Rolling, Bit 2: Idle |
92: | `+0x44` | 8 | `uint64_t` | `Q` | `debuffMask` | Mặt nạ bit các hiệu ứng xấu (Freeze, Shock, Bleed...) |
93: 
94: ---
95: 
96: ### 2.3. `EntityTelemetryData` (Kích thước: 88 Bytes, Số lượng: 256 phần tử)
97: 
98: Mỗi thực thể lân cận nhân vật (quái vật, đồ rơi, portal, NPC):
99: 
100: | Byte Offset | Độ Dài | Kiểu C++ | Kiểu Python | Tên Trường | Mô Tả |
101: | :---: | :---: | :--- | :--- | :--- | :--- |
102: | `+0x00` | 4 | `uint32_t` | `I` | `id` | ID thực thể duy nhất trong instance |
103: | `+0x04` | 2 | `uint16_t` | `H` | `type` | 1: Quái vật, 2: Vật phẩm rơi, 3: Cổng, 4: NPC |
104: | `+0x06` | 2 | `uint16_t` | `H` | `rarity` | 0: Normal/White, 1: Magic, 2: Rare, 3: Unique |
105: | `+0x08` | 4 | `float` | `f` | `posX` | Tọa độ X của thực thể |
106: | `+0x0C` | 4 | `float` | `f` | `posY` | Tọa độ Y của thực thể |
107: | `+0x10` | 4 | `float` | `f` | `posZ` | Tọa độ Z của thực thể |
108: | `+0x14` | 4 | `float` | `f` | `distanceToPlayer` | Khoảng cách Euclid tới người chơi |
109: | `+0x18` | 4 | `uint32_t` | `I` | `currentHP` | Máu hiện tại |
110: | `+0x1C` | 4 | `uint32_t` | `I` | `maxHP` | Máu tối đa |
111: | `+0x20` | 2 | `uint16_t` | `H` | `staggerProgress` | Thanh Stagger Poise (0 - 10000 = 0% - 100%) |
112: | `+0x22` | 2 | `uint16_t` | `H` | `currentAnimationId` | Mã hiệu ứng animation hiện tại |
113: | `+0x24` | 4 | `uint32_t` | `I` | `extraFlags` | Bit 0: Boss/Unique, Bit 1: Targetable, Bit 2: Dead |
114: | `+0x28` | 48 | `char[48]` | `48s` | `name` | Tên thực thể dạng chuỗi ASCII null-terminated |
115: 
116: ---
117: 
118: ### 2.4. `AreaTelemetryData` (Kích thước: 96 Bytes)
119: 
120: Thông tin bản đồ cập nhật từ `LogSensor`:
121: 
122: | Byte Offset | Độ Dài | Kiểu C++ | Kiểu Python | Tên Trường | Mô Tả |
123: | :---: | :---: | :--- | :--- | :--- | :--- |
124: | `+0x00` | 8 | `uint64_t` | `Q` | `areaLoadTimestampMs` | Thời điểm tải xong màn hình (Unix ms) |
125: | `+0x08` | 4 | `uint32_t` | `I` | `areaLevel` | Level của khu vực |
126: | `+0x0C` | 4 | `uint32_t` | `I` | `areaSeed` | Seed địa hình ngẫu nhiên của map |
127: | `+0x10` | 32 | `char[32]` | `32s` | `areaCode` | Mã kỹ thuật: "G1_1", "MapWorldsBeach"... |
128: | `+0x30` | 48 | `char[48]` | `48s` | `areaName` | Tên hiển thị: "The Riverbank", "Hideout"... |
129: 
130: ---
131: 
132: ### 2.5. `TelemetryPacket` (Tổng Kích Thước: 39,112 Bytes)
133: 
134: Gói tin dữ liệu snapshot hợp nhất từ C++ Core gửi sang Python Agent:
135: 
136: | Offset Trong Packet | Độ Dài | Thành Phần | Mô Tả |
137: | :---: | :---: | :--- | :--- |
138: | `0x0000` | 8 | `uint64_t snapshotId` | Bộ đếm snapshot ID tuần tự |
139: | `0x0008` | 8 | `uint64_t timestamp` | Thời điểm ghi snapshot (Unix ms) |
140: | `0x0010` | 84 | `PlayerTelemetryData player` | Tọa độ, hướng nhìn và HP/Mana/ES/Spirit/Ward |
141: | `0x0064` | 4 | `uint32_t entityCount` | Số lượng thực thể hợp lệ trong mảng (tối đa 256) |
142: | `0x0068` | 22,528 | `EntityTelemetryData entities[256]` | Mảng 256 thực thể (256 x 88 Bytes) |
143: | `0x5868` | 16,384 | `uint8_t terrainWalkability[16384]` | Lưới địa hình 128x128 (0: Cản, 1: Đi được) |
144: | `0x9868` | 96 | `AreaTelemetryData area` | Thông tin khu vực từ nhật ký game |
145: | **Tổng cộng** | **39,112 Bytes** | *(Nằm gọn trong slot 64 KB = 65,536 Bytes)* |

---

### 2.6. `CommandPacket` (Kích Thước: 36 Bytes, Stride: 64 Bytes, Offset: `0x300000`)

Gói tin chỉ thị vĩ mô từ Python Agent gửi xuống C++ Core Engine.

**Định dạng đóng gói Python Struct**:
```python
CMD_STRUCT_FORMAT = "<QIfffIII"  # Đúng chuẩn 36 bytes Little-Endian
```

| Byte Offset | Độ Dài | Kiểu C++ | Kiểu Python | Tên Trường | Mô Tả Chi Tiết |
| :---: | :---: | :--- | :--- | :--- | :--- |
| `0x00` | 8 | `uint64_t` | `Q` | `commandId` | ID định danh lệnh duy nhất tăng dần |
| `0x08` | 4 | `MacroOpCode` (`uint32_t`) | `I` | `opCode` | Mã lệnh thực thi (xem bảng OpCodes dưới) |
| `0x0C` | 4 | `float` | `f` | `targetX` | Tọa độ đích X |
| `0x10` | 4 | `float` | `f` | `targetY` | Tọa độ đích Y |
| `0x14` | 4 | `float` | `f` | `targetZ` | Tọa độ đích Z |
| `0x18` | 4 | `uint32_t` | `I` | `targetEntityId` | ID của thực thể cần tương tác hoặc tấn công |
| `0x1C` | 4 | `uint32_t` | `I` | `priority` | Mức ưu tiên thực thi (1=Thấp, 10=Cấp bách) |
| `0x20` | 4 | `uint32_t` | `I` | `timeoutMs` | Thời gian timeout hủy lệnh (ms) |

#### Bảng Tra Cứu Mã Lệnh (`MacroOpCode`):

| Giá Trị (uint32) | Tên OpCode | Chức Năng |
| :---: | :--- | :--- |
| `0` | `IDLE` | Trạng thái rỗi, không thực hiện hành động |
| `1` | `NAVIGATE_TO_COORD` | Di chuyển tới tọa độ đích qua đường cong Bézier / A* |
| `2` | `ENGAGE_TARGET` | Tấn công mục tiêu quái vật chỉ định |
| `3` | `PICKUP_ITEM` | Nhặt vật phẩm rơi chỉ định |
| `4` | `CAST_EMERGENCY_PORTAL` | Kích hoạt mở Town Portal 'T' |
| `5` | `EXECUTE_STASH_ROUTINE` | Mở hòm đồ và cất currency tự động |
| `6` | `EXECUTE_VENDOR_ROUTINE` | Tự động bán rác cho Vendor |
| `95` | `SET_MAP_SCENARIO` | Thiết lập kịch bản map (0: Boss Rush, 1: Atlas Quest, 2: Fast Clear, 3: Full Clear) |
| `96` | `TRIGGER_WORLD_MAP_TRAVEL` | Dịch chuyển bản đồ thế giới qua phím 'U' (Act, Zone index) |
| `97` | `TRIGGER_AUTO_LOGIN` | Yêu cầu kích hoạt máy trạng thái tự động kết nối lại |
| `98` | `RECALIBRATE_XYZ` | Yêu cầu quét và tái định vị con trỏ tọa độ XYZ |
| `99` | `PANIC_STOP` | Dừng khẩn cấp toàn bộ hoạt động của Core |

---

## 3. THUẬT TOÁN ĐIỀU PHỐI VÀ KIỂM CHỨNG NHỊ PHÂN

### 3.1. Hàng đợi Vòng SPSC Ring Buffer (16 Slots)
- Vị trí slot $i$ trong bộ nhớ:
  $$\text{SlotAddress}(i) = 0x300000 + (i \pmod{16}) \times 64$$
- Điều kiện kiểm tra đầy hàng đợi (Queue Full Check):
  $$\text{commandSequence} - \text{commandReadIndex} \ge 16$$
  Nếu điều kiện trên đúng, Producer **bắt buộc phải chờ**, không được ghi đè.

### 3.2. Mã nguồn Kiểm Chứng C++23 (`static_assert`)
Trong tệp `src/core/common/protocol.hpp`, tính toàn vẹn của giao thức được khóa chặt tại thời điểm biên dịch:

```cpp
static_assert(sizeof(SharedMemoryHeader) == 64, "Header phải đủ 64 bytes (1 cache line)");
static_assert(offsetof(SharedMemoryHeader, telemetrySequence) == 16, "Offset telemetrySequence = 16");
static_assert(offsetof(SharedMemoryHeader, commandSequence) == 24, "Offset commandSequence = 24");
static_assert(offsetof(SharedMemoryHeader, commandReadIndex) == 32, "Offset commandReadIndex = 32");
static_assert(offsetof(SharedMemoryHeader, lastCoreHeartbeat) == 40, "Offset lastCoreHeartbeat = 40");
static_assert(offsetof(SharedMemoryHeader, lastAgentHeartbeat) == 48, "Offset lastAgentHeartbeat = 48");
static_assert(offsetof(SharedMemoryHeader, reserved) == 56, "Offset reserved = 56");
static_assert(sizeof(PlayerTelemetryData) == 84, "Kích thước PlayerTelemetryData = 84 bytes (gồm Ward)");
static_assert(sizeof(EntityTelemetryData) == 88, "Kích thước EntityTelemetryData = 88 bytes");
static_assert(sizeof(AreaTelemetryData) == 96, "Kích thước AreaTelemetryData = 96 bytes");
static_assert(sizeof(CommandPacket) == 36, "Kích thước CommandPacket = 36 bytes");
static_assert(sizeof(TelemetryPacket) == 39112, "Kích thước TelemetryPacket = 39112 bytes (gồm Ward 8B & AreaTelemetryData 96B)");
static_assert(sizeof(TelemetryPacket) < TELEMETRY_SLOT_STRIDE, "Slot 64KB phải đủ chứa 1 snapshot");
```
