ygopro-msg-encode 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ENUM_FIX_SUMMARY.md +84 -0
- package/ENUM_UPDATE.md +219 -0
- package/README.md +28 -0
- package/STOC_CHAT_FIX.md +156 -0
- package/UTF16_STRING_FIX.md +190 -0
- package/dist/index.cjs +99 -6
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +90 -6
- package/dist/index.mjs.map +4 -4
- package/dist/src/protos/common/host-info.d.ts +2 -1
- package/dist/src/protos/ctos/proto/create-game.d.ts +2 -2
- package/dist/src/protos/ctos/proto/hand-result.d.ts +2 -1
- package/dist/src/protos/ctos/proto/join-game.d.ts +1 -1
- package/dist/src/protos/ctos/proto/kick.d.ts +2 -1
- package/dist/src/protos/ctos/proto/player-info.d.ts +1 -1
- package/dist/src/protos/ctos/proto/tp-result.d.ts +2 -1
- package/dist/src/protos/network-enums.d.ts +94 -0
- package/dist/src/protos/stoc/proto/error-msg.d.ts +2 -1
- package/dist/src/protos/stoc/proto/hs-player-enter.d.ts +1 -1
- package/dist/src/protos/stoc/proto/srvpro-roomlist.d.ts +2 -1
- package/index.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# GameRule 枚举修正总结
|
|
2
|
+
|
|
3
|
+
## 问题
|
|
4
|
+
|
|
5
|
+
之前错误地为 `HostInfo.rule` 字段创建了 `GameRule` 枚举,但实际上:
|
|
6
|
+
|
|
7
|
+
1. **`rule` 字段是 UI 下拉列表的索引**(0-5),不是实际的 OT 值
|
|
8
|
+
2. **实际的 OT 值**(1=OCG, 2=TCG, 4=OCG+TCG, 8=Traditional)是从 UI 索引映射过来的
|
|
9
|
+
3. **在协议中传输的是索引值**,而不是 OT 值本身
|
|
10
|
+
|
|
11
|
+
## 修正
|
|
12
|
+
|
|
13
|
+
### 1. 删除了 `GameRule` 枚举 ✅
|
|
14
|
+
|
|
15
|
+
从 `src/protos/network-enums.ts` 中删除:
|
|
16
|
+
```typescript
|
|
17
|
+
// 已删除
|
|
18
|
+
export enum GameRule {
|
|
19
|
+
OCG_ONLY = 1,
|
|
20
|
+
TCG_ONLY = 2,
|
|
21
|
+
OCG_TCG = 4,
|
|
22
|
+
TRADITIONAL = 8,
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. 将 `HostInfo.rule` 改回 `number` ✅
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
export class HostInfo {
|
|
30
|
+
@BinaryField('u32', 0)
|
|
31
|
+
lflist: number;
|
|
32
|
+
|
|
33
|
+
@BinaryField('u8', 4)
|
|
34
|
+
rule: number; // Rule index (0-5), maps to OT values in UI
|
|
35
|
+
|
|
36
|
+
@BinaryField('u8', 5)
|
|
37
|
+
mode: GameMode;
|
|
38
|
+
|
|
39
|
+
// ...
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 3. 更新了文档 ✅
|
|
44
|
+
|
|
45
|
+
- `README.md` - 移除了 `GameRule` 的引用
|
|
46
|
+
- `ENUM_UPDATE.md` - 添加了 `HostInfo.rule` 保持为 `number` 的说明
|
|
47
|
+
|
|
48
|
+
## UI 索引到 OT 值的映射(参考)
|
|
49
|
+
|
|
50
|
+
从 `gframe/game.cpp` 中可以看到映射关系:
|
|
51
|
+
|
|
52
|
+
| UI 索引 | OT 值 | 说明 |
|
|
53
|
+
|---------|-------|------|
|
|
54
|
+
| 0 | 1 | OCG Only (String 1483) |
|
|
55
|
+
| 1 | 2 | TCG Only (String 1484) |
|
|
56
|
+
| 2 | 8 | Traditional (String 1485) |
|
|
57
|
+
| 3 | 4 | OCG + TCG (String 1486) |
|
|
58
|
+
| 4+ | ? | 其他规则 |
|
|
59
|
+
|
|
60
|
+
**注意**:协议中传输的是索引(0-5),不是 OT 值(1, 2, 4, 8)。
|
|
61
|
+
|
|
62
|
+
## 保留的枚举
|
|
63
|
+
|
|
64
|
+
以下枚举保持不变:
|
|
65
|
+
|
|
66
|
+
1. `HandResult` - 猜拳结果
|
|
67
|
+
2. `TurnPlayerResult` - 先后手选择
|
|
68
|
+
3. `NetPlayerType` - 玩家类型/位置
|
|
69
|
+
4. `GameMode` - 游戏模式
|
|
70
|
+
5. `ErrorMessageType` - 错误消息类型
|
|
71
|
+
6. `DeckErrorType` - 卡组错误类型
|
|
72
|
+
7. `PlayerChangeState` - 玩家状态变化
|
|
73
|
+
8. `RoomStatus` - 房间状态
|
|
74
|
+
|
|
75
|
+
## 使用示例
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { YGOProCtosCreateGame, GameMode } from 'ygopro-msg-encode';
|
|
79
|
+
|
|
80
|
+
const createGame = new YGOProCtosCreateGame();
|
|
81
|
+
createGame.info.mode = GameMode.MATCH;
|
|
82
|
+
createGame.info.rule = 0; // UI 索引 0 (会映射到 OT=1, OCG Only)
|
|
83
|
+
createGame.info.duel_rule = 5; // MASTER_RULE_2020
|
|
84
|
+
```
|
package/ENUM_UPDATE.md
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# 枚举类型更新总结
|
|
2
|
+
|
|
3
|
+
本文档记录了将网络协议中的数字字段改为 TypeScript 枚举的更新。
|
|
4
|
+
|
|
5
|
+
## 更新原则
|
|
6
|
+
|
|
7
|
+
1. **仅更新网络协议相关的枚举**:CTOS 和 STOC 协议中的语义字段
|
|
8
|
+
2. **不修改已有常量**:MSG 协议中的字段(如 `phase`、`position`、`location`、`reason`)已在 `OcgcoreCommonConstants` 中定义,保持使用常量
|
|
9
|
+
3. **使用 TypeScript enum**:提供类型安全和更好的 IDE 支持
|
|
10
|
+
|
|
11
|
+
## 新增枚举类型
|
|
12
|
+
|
|
13
|
+
所有枚举定义在 `src/protos/network-enums.ts`:
|
|
14
|
+
|
|
15
|
+
### 1. HandResult - 猜拳结果
|
|
16
|
+
```typescript
|
|
17
|
+
enum HandResult {
|
|
18
|
+
ROCK = 1,
|
|
19
|
+
SCISSORS = 2,
|
|
20
|
+
PAPER = 3,
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
**使用位置**:`CTOS_HandResult.res`
|
|
24
|
+
|
|
25
|
+
### 2. TurnPlayerResult - 先后手选择
|
|
26
|
+
```typescript
|
|
27
|
+
enum TurnPlayerResult {
|
|
28
|
+
SECOND = 0,
|
|
29
|
+
FIRST = 1,
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
**使用位置**:`CTOS_TPResult.res`
|
|
33
|
+
|
|
34
|
+
### 3. NetPlayerType - 玩家类型/位置
|
|
35
|
+
```typescript
|
|
36
|
+
enum NetPlayerType {
|
|
37
|
+
PLAYER1 = 0,
|
|
38
|
+
PLAYER2 = 1,
|
|
39
|
+
PLAYER3 = 2,
|
|
40
|
+
PLAYER4 = 3,
|
|
41
|
+
PLAYER5 = 4,
|
|
42
|
+
PLAYER6 = 5,
|
|
43
|
+
OBSERVER = 7,
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
**使用位置**:
|
|
47
|
+
- `CTOS_Kick.pos`
|
|
48
|
+
|
|
49
|
+
### 4. ChatColor - 聊天消息颜色
|
|
50
|
+
```typescript
|
|
51
|
+
enum ChatColor {
|
|
52
|
+
LIGHTBLUE = 8,
|
|
53
|
+
RED = 11,
|
|
54
|
+
GREEN = 12,
|
|
55
|
+
BLUE = 13,
|
|
56
|
+
BABYBLUE = 14,
|
|
57
|
+
PINK = 15,
|
|
58
|
+
YELLOW = 16,
|
|
59
|
+
WHITE = 17,
|
|
60
|
+
GRAY = 18,
|
|
61
|
+
DARKGRAY = 19,
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
**说明**:用于 `STOC_Chat.player_type` 字段,但该字段保持为 `number` 类型,因为它可以是玩家类型(0-7)或聊天颜色(8-19)
|
|
65
|
+
|
|
66
|
+
### 5. GameMode - 游戏模式
|
|
67
|
+
```typescript
|
|
68
|
+
enum GameMode {
|
|
69
|
+
SINGLE = 0,
|
|
70
|
+
MATCH = 1,
|
|
71
|
+
TAG = 2,
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
**使用位置**:`HostInfo.mode`
|
|
75
|
+
|
|
76
|
+
### 6. ErrorMessageType - 错误消息类型
|
|
77
|
+
```typescript
|
|
78
|
+
enum ErrorMessageType {
|
|
79
|
+
JOINERROR = 1,
|
|
80
|
+
DECKERROR = 2,
|
|
81
|
+
SIDEERROR = 3,
|
|
82
|
+
VERERROR = 4,
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
**使用位置**:`STOC_ErrorMsg.msg`
|
|
86
|
+
|
|
87
|
+
### 7. DeckErrorType - 卡组错误类型
|
|
88
|
+
```typescript
|
|
89
|
+
enum DeckErrorType {
|
|
90
|
+
LFLIST = 1,
|
|
91
|
+
OCGONLY = 2,
|
|
92
|
+
TCGONLY = 3,
|
|
93
|
+
UNKNOWNCARD = 4,
|
|
94
|
+
CARDCOUNT = 5,
|
|
95
|
+
MAINCOUNT = 6,
|
|
96
|
+
EXTRACOUNT = 7,
|
|
97
|
+
SIDECOUNT = 8,
|
|
98
|
+
NOTAVAIL = 9,
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
**说明**:用于解析 `STOC_ErrorMsg.code` 的高 4 位(当 `msg == DECKERROR` 时)
|
|
102
|
+
|
|
103
|
+
### 8. PlayerChangeState - 玩家状态变化
|
|
104
|
+
```typescript
|
|
105
|
+
enum PlayerChangeState {
|
|
106
|
+
OBSERVE = 8,
|
|
107
|
+
READY = 9,
|
|
108
|
+
NOTREADY = 10,
|
|
109
|
+
LEAVE = 11,
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
**说明**:用于解析 `STOC_HS_PlayerChange.status` 的低 4 位
|
|
113
|
+
|
|
114
|
+
### 9. RoomStatus - 房间状态(SRVPro)
|
|
115
|
+
```typescript
|
|
116
|
+
enum RoomStatus {
|
|
117
|
+
WAITING = 0,
|
|
118
|
+
DUELING = 1,
|
|
119
|
+
SIDING = 2,
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
**使用位置**:`STOC_SRVPRO_ROOMLIST.room_status`
|
|
123
|
+
|
|
124
|
+
## 已更新的文件
|
|
125
|
+
|
|
126
|
+
### CTOS 协议
|
|
127
|
+
1. `src/protos/ctos/proto/hand-result.ts` - `res: HandResult`
|
|
128
|
+
2. `src/protos/ctos/proto/tp-result.ts` - `res: TurnPlayerResult`
|
|
129
|
+
3. `src/protos/ctos/proto/kick.ts` - `pos: NetPlayerType`
|
|
130
|
+
|
|
131
|
+
### STOC 协议
|
|
132
|
+
1. `src/protos/stoc/proto/error-msg.ts` - `msg: ErrorMessageType`
|
|
133
|
+
2. `src/protos/stoc/proto/chat.ts` - `player_type: number` (可以是 NetPlayerType 0-7 或 ChatColor 8-19)
|
|
134
|
+
3. `src/protos/stoc/proto/srvpro-roomlist.ts` - `room_status: RoomStatus`
|
|
135
|
+
|
|
136
|
+
### 公共结构
|
|
137
|
+
1. `src/protos/common/host-info.ts` - `mode: GameMode`
|
|
138
|
+
|
|
139
|
+
**注意**:`HostInfo.rule` 保持为 `number` 类型,因为它是UI下拉列表的索引(0-5),而不是实际的OT值。
|
|
140
|
+
|
|
141
|
+
### 主入口
|
|
142
|
+
1. `index.ts` - 导出所有枚举类型
|
|
143
|
+
|
|
144
|
+
## 未更改的字段
|
|
145
|
+
|
|
146
|
+
以下字段已在 `OcgcoreCommonConstants` 中有常量定义,因此**不改为枚举**:
|
|
147
|
+
|
|
148
|
+
### MSG 协议字段
|
|
149
|
+
- `MSG_HINT.type` - 使用 `HINT_*` 常量
|
|
150
|
+
- `MSG_CARD_HINT.type` - 使用 `HINT_*` 常量
|
|
151
|
+
- `MSG_PLAYER_HINT.type` - 使用 `PHINT_*` 常量
|
|
152
|
+
- `MSG_NEW_PHASE.phase` - 使用 `PHASE_*` 常量
|
|
153
|
+
- `MSG_MOVE.reason` - 使用 `REASON_*` 常量
|
|
154
|
+
- `MSG_POS_CHANGE.position` - 使用 `POS_*` 常量
|
|
155
|
+
- `MSG_SELECT_POSITION.positions` - 使用 `POS_*` 常量
|
|
156
|
+
- 所有包含 `location` 的字段 - 使用 `LOCATION_*` 常量(注:部分基础常量可能在 vendor 生成脚本中缺失)
|
|
157
|
+
|
|
158
|
+
### HostInfo 字段
|
|
159
|
+
- `HostInfo.duel_rule` - 使用 `MASTER_RULE3`, `NEW_MASTER_RULE`, `MASTER_RULE_2020` 等常量
|
|
160
|
+
|
|
161
|
+
## 使用示例
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import {
|
|
165
|
+
YGOProCtosHandResult,
|
|
166
|
+
HandResult,
|
|
167
|
+
YGOProCtosCreateGame,
|
|
168
|
+
GameMode,
|
|
169
|
+
NetPlayerType,
|
|
170
|
+
YGOProCtosKick,
|
|
171
|
+
} from 'ygopro-msg-encode';
|
|
172
|
+
|
|
173
|
+
// 创建猜拳消息
|
|
174
|
+
const handResult = new YGOProCtosHandResult();
|
|
175
|
+
handResult.res = HandResult.ROCK;
|
|
176
|
+
|
|
177
|
+
// 创建游戏
|
|
178
|
+
const createGame = new YGOProCtosCreateGame();
|
|
179
|
+
createGame.info.mode = GameMode.MATCH;
|
|
180
|
+
createGame.info.rule = 0; // Rule index (0-5), maps to OT values in UI
|
|
181
|
+
createGame.info.duel_rule = 5; // MASTER_RULE_2020 (from OcgcoreCommonConstants)
|
|
182
|
+
|
|
183
|
+
// 踢出玩家
|
|
184
|
+
const kick = new YGOProCtosKick();
|
|
185
|
+
kick.pos = NetPlayerType.PLAYER2;
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## 向后兼容性
|
|
189
|
+
|
|
190
|
+
由于 TypeScript enum 在运行时就是 `number`,这些改动是**完全向后兼容**的:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// 旧代码仍然可以工作
|
|
194
|
+
handResult.res = 1; // OK
|
|
195
|
+
|
|
196
|
+
// 但现在有类型检查和 IDE 自动补全
|
|
197
|
+
handResult.res = HandResult.ROCK; // Better!
|
|
198
|
+
handResult.res = 99; // TypeScript 会警告(如果启用严格模式)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 类型安全改进
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// 之前:没有类型检查
|
|
205
|
+
function handleHandResult(res: number) {
|
|
206
|
+
if (res === 1) { /* ... */ }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 现在:类型安全
|
|
210
|
+
function handleHandResult(res: HandResult) {
|
|
211
|
+
if (res === HandResult.ROCK) { /* ... */ }
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// IDE 会提供自动补全和类型检查
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## 测试
|
|
218
|
+
|
|
219
|
+
所有现有测试均通过,枚举更改不影响二进制序列化/反序列化逻辑。
|
package/README.md
CHANGED
|
@@ -517,6 +517,34 @@ npm run clean
|
|
|
517
517
|
- [Quick Reference](./QUICK_REFERENCE.md) - Quick API reference
|
|
518
518
|
- [Tests Migration](./TESTS_MIGRATION.md) - Testing guide
|
|
519
519
|
|
|
520
|
+
## Network Protocol Enums
|
|
521
|
+
|
|
522
|
+
This library provides TypeScript enums for network protocol fields with semantic meaning:
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
import {
|
|
526
|
+
HandResult, // Rock-paper-scissors result
|
|
527
|
+
TurnPlayerResult, // First/second turn selection
|
|
528
|
+
NetPlayerType, // Player position/type
|
|
529
|
+
ChatColor, // Chat message colors
|
|
530
|
+
GameMode, // Single/Match/Tag
|
|
531
|
+
ErrorMessageType, // Error message types
|
|
532
|
+
RoomStatus, // Room status (SRVPro)
|
|
533
|
+
} from 'ygopro-msg-encode';
|
|
534
|
+
|
|
535
|
+
// Example: Create a game with specific settings
|
|
536
|
+
const createGame = new YGOProCtosCreateGame();
|
|
537
|
+
createGame.info.mode = GameMode.MATCH;
|
|
538
|
+
createGame.info.rule = 0; // Rule index (0-5), maps to OT values in UI
|
|
539
|
+
|
|
540
|
+
// Example: Chat with color
|
|
541
|
+
const chat = new YGOProStocChat();
|
|
542
|
+
chat.player_type = ChatColor.RED; // Use color for system message
|
|
543
|
+
chat.msg = "Welcome to the game!";
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
**Note**: MSG protocol fields (like `phase`, `position`, `location`, `reason`) use constants from `OcgcoreCommonConstants` instead of enums, as they are automatically generated from YGOPro's `common.h`.
|
|
547
|
+
|
|
520
548
|
## Dependencies
|
|
521
549
|
|
|
522
550
|
- **Runtime Dependencies**:
|
package/STOC_CHAT_FIX.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# STOC_CHAT player_type 修正总结
|
|
2
|
+
|
|
3
|
+
## 问题
|
|
4
|
+
|
|
5
|
+
`STOC_Chat.player_type` 字段之前被定义为 `NetPlayerType` 枚举,但实际上该字段可以包含两种不同类型的值:
|
|
6
|
+
|
|
7
|
+
1. **玩家类型**(0-7):PLAYER1-6, OBSERVER
|
|
8
|
+
2. **聊天颜色**(8-19):用于系统消息或特殊格式的聊天
|
|
9
|
+
|
|
10
|
+
## 修正
|
|
11
|
+
|
|
12
|
+
### 1. 添加 ChatColor 枚举 ✅
|
|
13
|
+
|
|
14
|
+
在 `src/protos/network-enums.ts` 中添加:
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
/**
|
|
18
|
+
* Chat message colors (used in STOC_Chat.player_type)
|
|
19
|
+
*/
|
|
20
|
+
export enum ChatColor {
|
|
21
|
+
LIGHTBLUE = 8,
|
|
22
|
+
RED = 11,
|
|
23
|
+
GREEN = 12,
|
|
24
|
+
BLUE = 13,
|
|
25
|
+
BABYBLUE = 14,
|
|
26
|
+
PINK = 15,
|
|
27
|
+
YELLOW = 16,
|
|
28
|
+
WHITE = 17,
|
|
29
|
+
GRAY = 18,
|
|
30
|
+
DARKGRAY = 19,
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**来源**:`/home/nanahira/ygo/srvpro/data/constants.json` 中的 `COLORS` 定义
|
|
35
|
+
|
|
36
|
+
### 2. 将 player_type 改回 number ✅
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
export class YGOProStocChat extends YGOProStocBase {
|
|
40
|
+
static identifier = 0x19;
|
|
41
|
+
|
|
42
|
+
player_type: number; // NetPlayerType (0-7) or ChatColor (8-19)
|
|
43
|
+
msg: string;
|
|
44
|
+
|
|
45
|
+
// ...
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**原因**:该字段可以是玩家类型或聊天颜色,使用联合类型更合适。
|
|
50
|
+
|
|
51
|
+
### 3. 更新了文档 ✅
|
|
52
|
+
|
|
53
|
+
- `README.md` - 添加了 `ChatColor` 的使用示例
|
|
54
|
+
- `ENUM_UPDATE.md` - 说明了 `player_type` 保持为 `number` 的原因
|
|
55
|
+
|
|
56
|
+
## 使用场景
|
|
57
|
+
|
|
58
|
+
### 场景 1:玩家发送的聊天消息
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { YGOProStocChat, NetPlayerType } from 'ygopro-msg-encode';
|
|
62
|
+
|
|
63
|
+
const chat = new YGOProStocChat();
|
|
64
|
+
chat.player_type = NetPlayerType.PLAYER1; // 或直接使用 0
|
|
65
|
+
chat.msg = "Good game!";
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
在这种情况下,`player_type` 表示发送消息的玩家位置。
|
|
69
|
+
|
|
70
|
+
### 场景 2:系统消息或特殊颜色
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { YGOProStocChat, ChatColor } from 'ygopro-msg-encode';
|
|
74
|
+
|
|
75
|
+
const chat = new YGOProStocChat();
|
|
76
|
+
chat.player_type = ChatColor.RED; // 红色系统消息
|
|
77
|
+
chat.msg = "Game will start in 10 seconds!";
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
在这种情况下,`player_type` 表示聊天消息的颜色。
|
|
81
|
+
|
|
82
|
+
## duelclient.cpp 中的处理逻辑
|
|
83
|
+
|
|
84
|
+
从源码可以看到:
|
|
85
|
+
|
|
86
|
+
```cpp
|
|
87
|
+
uint16_t chat_player_type = BufferIO::Read<uint16_t>(pdata);
|
|
88
|
+
// ...
|
|
89
|
+
int player = chat_player_type;
|
|
90
|
+
auto play_sound = false;
|
|
91
|
+
if(player < 4) { // 玩家消息(0-3)
|
|
92
|
+
auto localplayer = mainGame->ChatLocalPlayer(player);
|
|
93
|
+
player = localplayer & 0xf;
|
|
94
|
+
if(!(localplayer & 0x10))
|
|
95
|
+
play_sound = true;
|
|
96
|
+
if(play_sound && mainGame->chkIgnore1->isChecked())
|
|
97
|
+
break;
|
|
98
|
+
} else { // 系统消息或特殊颜色(>= 4)
|
|
99
|
+
if(player == 8) { //system custom message.
|
|
100
|
+
// ...
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
客户端根据 `player_type` 的值:
|
|
106
|
+
- **< 4**:当作玩家消息处理
|
|
107
|
+
- **>= 4**:当作系统消息或特殊格式处理(可能包含颜色信息)
|
|
108
|
+
|
|
109
|
+
## 颜色值映射(参考)
|
|
110
|
+
|
|
111
|
+
| 值 | 常量名 | 说明 |
|
|
112
|
+
|----|--------|------|
|
|
113
|
+
| 8 | LIGHTBLUE | 浅蓝色 |
|
|
114
|
+
| 11 | RED | 红色 |
|
|
115
|
+
| 12 | GREEN | 绿色 |
|
|
116
|
+
| 13 | BLUE | 蓝色 |
|
|
117
|
+
| 14 | BABYBLUE | 婴儿蓝 |
|
|
118
|
+
| 15 | PINK | 粉色 |
|
|
119
|
+
| 16 | YELLOW | 黄色 |
|
|
120
|
+
| 17 | WHITE | 白色 |
|
|
121
|
+
| 18 | GRAY | 灰色 |
|
|
122
|
+
| 19 | DARKGRAY | 深灰色 |
|
|
123
|
+
|
|
124
|
+
## 类型安全建议
|
|
125
|
+
|
|
126
|
+
虽然 `player_type` 是 `number` 类型,但可以使用类型守卫来确保类型安全:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
function isPlayerType(value: number): value is NetPlayerType {
|
|
130
|
+
return value >= 0 && value <= 7 && value !== 6;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function isChatColor(value: number): value is ChatColor {
|
|
134
|
+
return value >= 8 && value <= 19;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 使用
|
|
138
|
+
const chat = new YGOProStocChat();
|
|
139
|
+
chat.fromFullPayload(data);
|
|
140
|
+
|
|
141
|
+
if (isPlayerType(chat.player_type)) {
|
|
142
|
+
console.log('Player message from:', NetPlayerType[chat.player_type]);
|
|
143
|
+
} else if (isChatColor(chat.player_type)) {
|
|
144
|
+
console.log('System message with color:', ChatColor[chat.player_type]);
|
|
145
|
+
} else {
|
|
146
|
+
console.log('Unknown player_type:', chat.player_type);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 总结
|
|
151
|
+
|
|
152
|
+
`STOC_Chat.player_type` 是一个多用途字段:
|
|
153
|
+
- ✅ 保持为 `number` 类型以支持两种不同的语义
|
|
154
|
+
- ✅ 提供 `NetPlayerType` 和 `ChatColor` 枚举供参考
|
|
155
|
+
- ✅ 用户可以根据值的范围判断其含义
|
|
156
|
+
- ✅ 完全向后兼容,不影响现有代码
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# UTF-16 字符串字段修复总结
|
|
2
|
+
|
|
3
|
+
## 问题
|
|
4
|
+
|
|
5
|
+
多个网络协议中的字段被错误地定义为 `u16` 数组(`number[]`),但实际上它们应该是 UTF-16 字符串(`string`)。
|
|
6
|
+
|
|
7
|
+
这些字段在源码 `network.h` 中都定义为 `uint16_t name[20]` 或 `uint16_t pass[20]`,表示 UTF-16 编码的字符串,而不是整数数组。
|
|
8
|
+
|
|
9
|
+
## 修复的字段
|
|
10
|
+
|
|
11
|
+
### 1. CTOS_PlayerInfo (`src/protos/ctos/proto/player-info.ts`)
|
|
12
|
+
|
|
13
|
+
**修复前**:
|
|
14
|
+
```typescript
|
|
15
|
+
@BinaryField('u16', 0, 20)
|
|
16
|
+
name: number[];
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**修复后**:
|
|
20
|
+
```typescript
|
|
21
|
+
@BinaryField('utf16', 0, 20)
|
|
22
|
+
name: string;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. CTOS_JoinGame (`src/protos/ctos/proto/join-game.ts`)
|
|
26
|
+
|
|
27
|
+
**修复前**:
|
|
28
|
+
```typescript
|
|
29
|
+
@BinaryField('u16', 8, 20)
|
|
30
|
+
pass: number[];
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**修复后**:
|
|
34
|
+
```typescript
|
|
35
|
+
@BinaryField('utf16', 8, 20)
|
|
36
|
+
pass: string;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 3. CTOS_CreateGame (`src/protos/ctos/proto/create-game.ts`)
|
|
40
|
+
|
|
41
|
+
**修复前**:
|
|
42
|
+
```typescript
|
|
43
|
+
@BinaryField('u16', 20, 20)
|
|
44
|
+
name: number[];
|
|
45
|
+
|
|
46
|
+
@BinaryField('u16', 60, 20)
|
|
47
|
+
pass: number[];
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**修复后**:
|
|
51
|
+
```typescript
|
|
52
|
+
@BinaryField('utf16', 20, 20)
|
|
53
|
+
name: string;
|
|
54
|
+
|
|
55
|
+
@BinaryField('utf16', 60, 20)
|
|
56
|
+
pass: string;
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 4. STOC_HS_PlayerEnter (`src/protos/stoc/proto/hs-player-enter.ts`)
|
|
60
|
+
|
|
61
|
+
**修复前**:
|
|
62
|
+
```typescript
|
|
63
|
+
@BinaryField('u16', 0, 20)
|
|
64
|
+
name: number[];
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**修复后**:
|
|
68
|
+
```typescript
|
|
69
|
+
@BinaryField('utf16', 0, 20)
|
|
70
|
+
name: string;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 测试文件修复
|
|
74
|
+
|
|
75
|
+
同时更新了 `tests/ctos-stoc.spec.ts` 中的相关测试:
|
|
76
|
+
|
|
77
|
+
**修复前**:
|
|
78
|
+
```typescript
|
|
79
|
+
playerInfo.name = Array.from({ length: 20 }, (_, i) =>
|
|
80
|
+
i < 4 ? 0x0041 + i : 0,
|
|
81
|
+
); // "ABCD"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**修复后**:
|
|
85
|
+
```typescript
|
|
86
|
+
playerInfo.name = 'ABCD';
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 源码参考
|
|
90
|
+
|
|
91
|
+
根据 `gframe/network.h` 的定义:
|
|
92
|
+
|
|
93
|
+
```cpp
|
|
94
|
+
struct CTOS_PlayerInfo {
|
|
95
|
+
uint16_t name[20]{}; // UTF-16 string, 20 characters max
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
struct CTOS_CreateGame {
|
|
99
|
+
HostInfo info;
|
|
100
|
+
uint16_t name[20]{}; // UTF-16 string, 20 characters max
|
|
101
|
+
uint16_t pass[20]{}; // UTF-16 string, 20 characters max
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
struct CTOS_JoinGame {
|
|
105
|
+
uint16_t version{};
|
|
106
|
+
// byte padding[2]
|
|
107
|
+
uint32_t gameid{};
|
|
108
|
+
uint16_t pass[20]{}; // UTF-16 string, 20 characters max
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
struct STOC_HS_PlayerEnter {
|
|
112
|
+
uint16_t name[20]{}; // UTF-16 string, 20 characters max
|
|
113
|
+
uint8_t pos{};
|
|
114
|
+
// byte padding[1]
|
|
115
|
+
};
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
所有这些 `uint16_t array[20]` 字段都是用于存储 UTF-16 编码的字符串,最多 20 个字符(包括 null 终止符)。
|
|
119
|
+
|
|
120
|
+
## 使用示例
|
|
121
|
+
|
|
122
|
+
### 修复前(错误):
|
|
123
|
+
```typescript
|
|
124
|
+
const playerInfo = new YGOProCtosPlayerInfo();
|
|
125
|
+
playerInfo.name = [0x0041, 0x0042, 0x0043, 0x0044, 0, 0, ...]; // "ABCD"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 修复后(正确):
|
|
129
|
+
```typescript
|
|
130
|
+
const playerInfo = new YGOProCtosPlayerInfo();
|
|
131
|
+
playerInfo.name = 'ABCD'; // 直接使用字符串
|
|
132
|
+
|
|
133
|
+
const createGame = new YGOProCtosCreateGame();
|
|
134
|
+
createGame.name = 'MyGame';
|
|
135
|
+
createGame.pass = 'password123';
|
|
136
|
+
|
|
137
|
+
const joinGame = new YGOProCtosJoinGame();
|
|
138
|
+
joinGame.version = 0x1353;
|
|
139
|
+
joinGame.gameid = 12345;
|
|
140
|
+
joinGame.pass = 'password123';
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 二进制格式
|
|
144
|
+
|
|
145
|
+
UTF-16 字符串在二进制中的存储方式:
|
|
146
|
+
- 每个字符占 2 字节(UTF-16LE 编码)
|
|
147
|
+
- 字符串以 null 终止符(`\0`, `0x0000`)结尾
|
|
148
|
+
- 固定长度字段会被 null 字符填充到指定长度
|
|
149
|
+
|
|
150
|
+
例如,字符串 `"ABCD"` 在 20 字符的字段中:
|
|
151
|
+
```
|
|
152
|
+
41 00 42 00 43 00 44 00 00 00 00 00 ... (共 40 字节)
|
|
153
|
+
A B C D \0 \0 ...
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 向后兼容性
|
|
157
|
+
|
|
158
|
+
这个修复是**破坏性变更**,因为字段类型从 `number[]` 改为了 `string`。
|
|
159
|
+
|
|
160
|
+
**升级指南**:
|
|
161
|
+
```typescript
|
|
162
|
+
// 旧代码
|
|
163
|
+
playerInfo.name = [0x0041, 0x0042, 0x0043, 0x0044, ...];
|
|
164
|
+
|
|
165
|
+
// 新代码
|
|
166
|
+
playerInfo.name = 'ABCD';
|
|
167
|
+
|
|
168
|
+
// 如果需要从旧格式转换
|
|
169
|
+
const oldName = [0x0041, 0x0042, 0x0043, 0x0044, 0, ...];
|
|
170
|
+
const newName = String.fromCharCode(...oldName.filter(c => c !== 0));
|
|
171
|
+
playerInfo.name = newName;
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 验证
|
|
175
|
+
|
|
176
|
+
所有测试通过:
|
|
177
|
+
- ✅ Binary serialization/deserialization tests
|
|
178
|
+
- ✅ CTOS/STOC protocol tests
|
|
179
|
+
- ✅ Round-trip encoding/decoding tests
|
|
180
|
+
|
|
181
|
+
## 总结
|
|
182
|
+
|
|
183
|
+
共修复了 **4 个文件** 中的 **5 个字段**:
|
|
184
|
+
1. `CTOS_PlayerInfo.name` - 玩家名称
|
|
185
|
+
2. `CTOS_JoinGame.pass` - 游戏密码
|
|
186
|
+
3. `CTOS_CreateGame.name` - 游戏名称
|
|
187
|
+
4. `CTOS_CreateGame.pass` - 游戏密码
|
|
188
|
+
5. `STOC_HS_PlayerEnter.name` - 玩家名称
|
|
189
|
+
|
|
190
|
+
所有字段都从 `@BinaryField('u16', ..., 20)` + `number[]` 改为 `@BinaryField('utf16', ..., 20)` + `string`。
|