add chatSystem_flow
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
# Luồng Hoạt Động Của Hệ Thống Chat (C#)
|
||||
|
||||
Tài liệu này tổng hợp luồng xử lý của hệ thống Chat S2C và C2S trong client, dựa trên hai hàm cốt lõi `SendChatData` và `OnPrtcChatMessage` thuộc lớp `GameSession.cs`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Luồng Gửi Tin Nhắn (C2S) - Hàm `SendChatData`
|
||||
|
||||
Hàm này được gọi khi người chơi nhập tin nhắn ở Local và muốn gửi lên Server (hoặc sử dụng vật phẩm liên quan đến Chat). Đặc biệt, **Server sẽ KHÔNG tự động gửi lại (echo) tin nhắn cho người gửi**, do đó client phải chủ động tự hiển thị tin nhắn của chính mình lên UI.
|
||||
|
||||
**Các bước xử lý:**
|
||||
|
||||
1. **Khởi tạo và đóng gói dữ liệu:**
|
||||
- Tạo gói tin C2S `publicchat` và thiết lập các thông số cơ bản (`Channel`, `Roleid` của người gửi).
|
||||
|
||||
2. **Xử lý đính kèm vật phẩm (nếu có):**
|
||||
- **Vật phẩm thẻ (`GENERALCARD_PACK`):** Nếu tin nhắn được gửi cùng một thẻ (card), hệ thống sẽ tìm trong túi đồ client (Client Pack) theo slot (`iSlot`), lấy ra `cardId` và chèn byte data lệnh `CHAT_GENERALCARD_COLLECTION` vào giao thức.
|
||||
- **Vật phẩm trang bị thông thường:** Nếu `iPack` và `iSlot` hợp lệ, đóng gói định dạng `CHAT_EQUIP_ITEM` gồm vị trí túi `where` và vị trí slot `index` chèn vào gói tin.
|
||||
|
||||
3. **Gửi gói tin lên Server:**
|
||||
- Tin nhắn chuỗi (`szMsg`) được mã hóa sang `UTF-16 LE` (Unicode trong C#) và lưu vào `Msg` (kiểu Octets).
|
||||
- Gọi `SendProtocol(p)` để đẩy dữ liệu xuống tầng Network và gửi đi.
|
||||
|
||||
4. **Hiển thị tin nhắn Local ngay lập tức:**
|
||||
- Xác định `nEmotionSet` (nếu có), ví dụ kênh **Super Far Cry** sẽ có hiệu ứng bong bóng đầu đặc biệt (trim 8 character cuối của text).
|
||||
- **Bong bóng đầu (Head Bubble):** Áp dụng nếu kênh thuộc dạng lân cận (`GP_CHAT_LOCAL`, `GP_CHAT_FARCRY`, `GP_CHAT_SUPERFARCRY`, `GP_CHAT_BATTLE`, `GP_CHAT_COUNTRY`).
|
||||
- Lấy tên Host Player, ghép chuỗi với định dạng text của hệ thống (sử dụng cố định `FIXMSG_CHAT`).
|
||||
- Gọi `EC_Game.GetGameRun().AddChatMessage(...)`: Hàm này sẽ đẩy dữ liệu lên UI Chat Box và tự động xử lý bong bóng trên đầu nhân vật.
|
||||
|
||||
---
|
||||
|
||||
## 2. Luồng Nhận Tin Nhắn (S2C) - Hàm `OnPrtcChatMessage`
|
||||
|
||||
Hàm này nhận giao thức `chatmessage` từ Server và xử lý việc hiển thị lên kênh chat, bong bóng đầu player/NPC.
|
||||
Vì hàm OnPrtcChatMessage đang quá lớn nên tôi chia ra thêm 1 class Chat_GameSession để xử lý các logic nhỏ.
|
||||
**Các bước xử lý:**
|
||||
|
||||
1. **Tiền xử lý và Lọc (Filter):**
|
||||
- Kiểm tra cấp độ người gửi qua `Chat_GameSession.ShouldBlockByLevel()`. Nếu bị chặn, hàm ngắt ngang và bỏ qua tin nhắn.
|
||||
- Tạo `EC_IvtrItem` (Chat Item) từ dải byte đính kèm (nếu có vật phẩm).
|
||||
- Lọc các tag mã hóa không hợp lệ thông qua `AUICommon.FilterInvalidTags`.
|
||||
- Chạy qua các policy chặn lọc hệ thống (`Chat_GameSession.PolicyResolver()`) để ra chuỗi tinh chỉnh cuối cùng (`szMsg`).
|
||||
|
||||
2. **Phân loại Channel và RoleID để xử lý:**
|
||||
|
||||
Hệ thống phân chia theo 3 nhóm luồng chính:
|
||||
|
||||
### Nhóm A: Broadcast, System, hoặc System Role (Srcroleid == 0)
|
||||
- Nếu `Channel` là `GP_CHAT_SYSTEM` và có `Srcroleid > 0`, tin nhắn thuộc các thông báo riêng biệt của cục bộ tính năng, được chia nhánh theo `Srcroleid` (Hardcode ID):
|
||||
- **ID = 1,2,3,4,6,7:** Bản tin Chiến trường (Battle Message).
|
||||
- **ID = 18..22:** Bản tin Đấu giá (Auction Message). Gọi `ChatMessageEvent` đưa lên kênh system.
|
||||
- **ID = 24:** Bản tin Nhiệm vụ (Task Message).
|
||||
- **ID = 29..45:** Bản tin Pháo đài, Lãnh thổ (Fortress Message).
|
||||
- **ID = 46..49:** Bản tin Quốc chiến (Country Battle).
|
||||
- **ID = 50..59:** Bản tin Vua (King Chat).
|
||||
- **ID = 60..64:** Bản tin PVP Bang hội (Faction PVP).
|
||||
*(Lưu ý: Một số hàm của luồng C++ đang bị ẩn/ẩn implementation, nếu cần data pending chưa có sẽ hoãn và chờ lần gọi lại (`bCalledagain`).)*
|
||||
- Nếu không thuộc các ID đặc biệt trên, hệ thống đơn giản là phát ra `ChatMessageEvent` đẩy thẳng tin nhắn lên luồng EventBus.
|
||||
|
||||
### Nhóm B: Instance Channel (`GP_CHAT_INSTANCE` & `Srcroleid == 1`)
|
||||
- Tin nhắn riêng của Phó bản, hiện tại được thiết kế để ném vào luồng `AddHeartBeatHint` ở UIMan.
|
||||
|
||||
### Nhóm C: Dành cho người chơi khác (Player) hoặc NPC
|
||||
*Sử dụng chuỗi định dạng lấy từ `FixedMsg` kết hợp tên người / NPC.*
|
||||
|
||||
- **Nếu là người chơi (`ISPLAYERID`):**
|
||||
- Request tên người chơi `GetPlayerName`.
|
||||
- **Thiếu Data:** Nếu không tìm thấy tên player trong cache, tin nhắn yêu cầu Server lấy tên (`AddChatPlayerID`) và **treo** giao thức chat này vào Pending Protoco (`AddElemForPendingProtocols`). Giao thức này sẽ tự kích hoạt lại ở Tick sau khi Server trả về tên.
|
||||
- **Có Data:** Xây dựng câu (kiểu `[Kênh] Player: Message`), gọi `AddChatMessage(...)` đẩy vào hộp thoại Chat và bắn bong bóng đầu tự động.
|
||||
|
||||
- **Nếu là NPC (`ISNPCID`):**
|
||||
- Tìm kiếm `CECNPC` từ danh sách `NPCMan`.
|
||||
- Dựng chuỗi bằng format `FIXMSG_CHAT2`.
|
||||
- Gọi `AddChatMessage()` cho chat panel và `EventBus.Publish(ChatMessageEvent)` cho UI liên quan.
|
||||
- Lọc riêng Name Flag trên đầu NPC, rồi gán nội dung bằng hàm `pNPC.SetLastSaidWords(szMsg)` để hiện Bubble Text trên mô hình 3D NPC.
|
||||
|
||||
---
|
||||
|
||||
## 3. Phân Tích Khác Biệt Giữa C++ và C# (Triển khai `OnPrtcChatMessage`)
|
||||
|
||||
Trong quá trình chuyển đổi (port) hệ thống Chat từ C++ sang C# (Unity), kiến trúc đã được thay đổi từ **mô hình liên kết chặt chẽ (Tight Coupling)** sang **mô hình hướng sự kiện (Event-Driven)** nhằm tách biệt hoàn toàn tầng Network và tầng UI/Logic Game.
|
||||
|
||||
Dưới đây là các điểm khác biệt chính:
|
||||
|
||||
| Tiêu chí | C++ (`EC_GameSession.cpp`) | C# Unity (`GameSession.cs` & `UIPlayer.cs`) |
|
||||
| :--- | :--- | :--- |
|
||||
| **Kiến trúc liên kết (Giao tiếp với mô hình 3D)** | Tầng Network gọi trực tiếp vào hệ thống quản lý Game World. Code chủ động tìm kiếm player: `CECPlayer *pPlayer = GetWorld()->GetPlayerMan()->GetPlayer(...)` rồi gọi trực tiếp hàm `pPlayer->SetLastSaidWords(strTemp, p->emotion, pItem);`. | Gọi thông qua kiến trúc kênh Pub/Sub: `EventBus.PublishChannel(p.Srcroleid, new EventChatMessageOnTopPlayer(p.Srcroleid, strMsg));`. Tầng Network không cần biết đối tượng 3D có tồn tại hay không. |
|
||||
| **Bong bóng Chat trên đầu (Head Bubble)** | Đối tượng `CECPlayer` tự tính toán, lưu trữ Text và dựa vào Tick update trung tâm C++ để hiển thị/tắt text sau một khoảng thời gian. | Giao diện hiển thị `UIPlayer.cs` (gắn trên Prefab nhân vật) tự đăng ký sự kiện (`EventBus.SubscribeChannel`). Khi nhận event, tự đổi chuỗi `TextMeshProUGUI`, hiện Text và gọi một `UniTask` (`HideChatAsync`) độc lập để tự động tắt sau 5 giây. |
|
||||
| **Xử lý Thread/Đa luồng** | Toàn bộ Network và Logic xử lý trên chung một luồng chính đồng bộ. | Tầng Network (nhận Packet) và tầng UI hiển thị Unity có thể bất đồng bộ. Vì vậy C# dùng `ChatThreadDispatcher.Instance.Post(...)` trong `UIPlayer.SetChatMessage` để đồng bộ an toàn việc gán giá trị UI lên luồng chính của Unity. |
|
||||
| **Bảo trì / Mở rộng** | Gắn chặt UI với Data Network. Nếu UI thay đổi, sửa Network code. | Decoupled (Giảm kết dính). Việc hiển thị UI chat (khung Chat Panel lẫn Head Bubble trên đầu nhân vật) hoạt động độc lập và chỉ "lắng nghe" dữ liệu. |
|
||||
|
||||
**Ví Dụ Chi Tiết (Luồng Head Bubble Player):**
|
||||
* **C++:** Trong `OnPrtcChatMessage`, code thực hiện:
|
||||
`pPlayer->SetLastSaidWords(strTemp, p->emotion, pItem);`
|
||||
Tức là bắt ép đối tượng thực hiện cập nhật UI.
|
||||
* **C#:** Trong `GameSession.cs` (hoặc bên trong `AddChatMessage`), hệ thống gọi:
|
||||
`EventBus.PublishChannel(p.Srcroleid, new EventChatMessageOnTopPlayer(p.Srcroleid, strMsg));`
|
||||
Tương ứng bên kia, trong `UIPlayer.cs` chạy ở hàm `Start()`:
|
||||
`EventBus.SubscribeChannel<EventChatMessageOnTopPlayer>(hostplayer.GetCharacterID(), SetChatMessage);`
|
||||
Khi event kích hoạt, hàm `SetChatMessage` sẽ nhận Context và gỡ nó ra gán vào Text Mesh UI một cách an toàn.
|
||||
Reference in New Issue
Block a user