ygopro-msg-encode 1.0.1

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.
Files changed (178) hide show
  1. package/.eslintignore +4 -0
  2. package/.eslintrc.js +25 -0
  3. package/.prettierrc +4 -0
  4. package/AGENTS.md +12 -0
  5. package/CHANGES.md +270 -0
  6. package/CTOS_STOC_IMPLEMENTATION.md +279 -0
  7. package/FINAL_SUMMARY.md +357 -0
  8. package/FULL_PAYLOAD_SUMMARY.md +491 -0
  9. package/FULL_PAYLOAD_UPDATE.md +598 -0
  10. package/IMPLEMENTATION_SUMMARY.md +294 -0
  11. package/LICENSE +22 -0
  12. package/MSG_IMPLEMENTATION_SUMMARY.md +358 -0
  13. package/OPPONENT_VIEW_SUMMARY.md +387 -0
  14. package/PROJECT_COMPLETE.md +565 -0
  15. package/QUICK_REFERENCE.md +352 -0
  16. package/README.md +494 -0
  17. package/REAL_IP_STRING_UPDATE.md +289 -0
  18. package/TESTS_MIGRATION.md +342 -0
  19. package/VARIABLE_LENGTH_STRINGS.md +224 -0
  20. package/VARIABLE_LENGTH_UPDATE.md +229 -0
  21. package/dist/index.cjs +5131 -0
  22. package/dist/index.cjs.map +7 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.mjs +5185 -0
  25. package/dist/index.mjs.map +7 -0
  26. package/dist/src/binary/binary-meta.d.ts +18 -0
  27. package/dist/src/binary/fill-binary-fields.d.ts +2 -0
  28. package/dist/src/metadata.d.ts +10 -0
  29. package/dist/src/proto-base/payload-base.d.ts +8 -0
  30. package/dist/src/proto-base/registry-base.d.ts +13 -0
  31. package/dist/src/protos/common/host-info.d.ts +12 -0
  32. package/dist/src/protos/common/index.d.ts +1 -0
  33. package/dist/src/protos/ctos/base.d.ts +17 -0
  34. package/dist/src/protos/ctos/index.d.ts +3 -0
  35. package/dist/src/protos/ctos/proto/chat.d.ts +9 -0
  36. package/dist/src/protos/ctos/proto/create-game.d.ts +8 -0
  37. package/dist/src/protos/ctos/proto/external-address.d.ts +12 -0
  38. package/dist/src/protos/ctos/proto/hand-result.d.ts +5 -0
  39. package/dist/src/protos/ctos/proto/hs-notready.d.ts +4 -0
  40. package/dist/src/protos/ctos/proto/hs-ready.d.ts +4 -0
  41. package/dist/src/protos/ctos/proto/hs-start.d.ts +4 -0
  42. package/dist/src/protos/ctos/proto/hs-toduelist.d.ts +4 -0
  43. package/dist/src/protos/ctos/proto/hs-toobserver.d.ts +4 -0
  44. package/dist/src/protos/ctos/proto/index.d.ts +19 -0
  45. package/dist/src/protos/ctos/proto/join-game.d.ts +7 -0
  46. package/dist/src/protos/ctos/proto/kick.d.ts +5 -0
  47. package/dist/src/protos/ctos/proto/leave-game.d.ts +4 -0
  48. package/dist/src/protos/ctos/proto/player-info.d.ts +5 -0
  49. package/dist/src/protos/ctos/proto/request-field.d.ts +4 -0
  50. package/dist/src/protos/ctos/proto/response.d.ts +7 -0
  51. package/dist/src/protos/ctos/proto/surrender.d.ts +4 -0
  52. package/dist/src/protos/ctos/proto/time-confirm.d.ts +4 -0
  53. package/dist/src/protos/ctos/proto/tp-result.d.ts +5 -0
  54. package/dist/src/protos/ctos/proto/update-deck.d.ts +11 -0
  55. package/dist/src/protos/ctos/registry.d.ts +3 -0
  56. package/dist/src/protos/msg/base.d.ts +9 -0
  57. package/dist/src/protos/msg/index.d.ts +3 -0
  58. package/dist/src/protos/msg/proto/add-counter.d.ts +9 -0
  59. package/dist/src/protos/msg/proto/announce-attrib.d.ts +7 -0
  60. package/dist/src/protos/msg/proto/announce-card.d.ts +7 -0
  61. package/dist/src/protos/msg/proto/announce-number.d.ts +7 -0
  62. package/dist/src/protos/msg/proto/announce-race.d.ts +7 -0
  63. package/dist/src/protos/msg/proto/attack-disabled.d.ts +4 -0
  64. package/dist/src/protos/msg/proto/attack.d.ts +12 -0
  65. package/dist/src/protos/msg/proto/battle.d.ts +18 -0
  66. package/dist/src/protos/msg/proto/become-target.d.ts +6 -0
  67. package/dist/src/protos/msg/proto/cancel-target.d.ts +11 -0
  68. package/dist/src/protos/msg/proto/card-hint.d.ts +10 -0
  69. package/dist/src/protos/msg/proto/card-query.d.ts +38 -0
  70. package/dist/src/protos/msg/proto/card-target.d.ts +11 -0
  71. package/dist/src/protos/msg/proto/chain-disabled.d.ts +5 -0
  72. package/dist/src/protos/msg/proto/chain-end.d.ts +4 -0
  73. package/dist/src/protos/msg/proto/chain-negated.d.ts +5 -0
  74. package/dist/src/protos/msg/proto/chain-solved.d.ts +5 -0
  75. package/dist/src/protos/msg/proto/chain-solving.d.ts +5 -0
  76. package/dist/src/protos/msg/proto/chained.d.ts +5 -0
  77. package/dist/src/protos/msg/proto/chaining.d.ts +12 -0
  78. package/dist/src/protos/msg/proto/confirm-cards.d.ts +16 -0
  79. package/dist/src/protos/msg/proto/confirm-decktop.d.ts +14 -0
  80. package/dist/src/protos/msg/proto/confirm-extratop.d.ts +14 -0
  81. package/dist/src/protos/msg/proto/damage-step-end.d.ts +4 -0
  82. package/dist/src/protos/msg/proto/damage-step-start.d.ts +4 -0
  83. package/dist/src/protos/msg/proto/damage.d.ts +6 -0
  84. package/dist/src/protos/msg/proto/deck-top.d.ts +8 -0
  85. package/dist/src/protos/msg/proto/draw.d.ts +8 -0
  86. package/dist/src/protos/msg/proto/equip.d.ts +12 -0
  87. package/dist/src/protos/msg/proto/field-disabled.d.ts +5 -0
  88. package/dist/src/protos/msg/proto/flipsummoned.d.ts +4 -0
  89. package/dist/src/protos/msg/proto/flipsummoning.d.ts +9 -0
  90. package/dist/src/protos/msg/proto/hand-res.d.ts +5 -0
  91. package/dist/src/protos/msg/proto/hint.d.ts +7 -0
  92. package/dist/src/protos/msg/proto/index.d.ts +85 -0
  93. package/dist/src/protos/msg/proto/lpupdate.d.ts +6 -0
  94. package/dist/src/protos/msg/proto/match-kill.d.ts +5 -0
  95. package/dist/src/protos/msg/proto/missed-effect.d.ts +7 -0
  96. package/dist/src/protos/msg/proto/move.d.ts +14 -0
  97. package/dist/src/protos/msg/proto/new-phase.d.ts +5 -0
  98. package/dist/src/protos/msg/proto/new-turn.d.ts +5 -0
  99. package/dist/src/protos/msg/proto/pay-lpcost.d.ts +6 -0
  100. package/dist/src/protos/msg/proto/player-hint.d.ts +7 -0
  101. package/dist/src/protos/msg/proto/pos-change.d.ts +12 -0
  102. package/dist/src/protos/msg/proto/random-selected.d.ts +7 -0
  103. package/dist/src/protos/msg/proto/recover.d.ts +6 -0
  104. package/dist/src/protos/msg/proto/reload-field.d.ts +36 -0
  105. package/dist/src/protos/msg/proto/remove-counter.d.ts +9 -0
  106. package/dist/src/protos/msg/proto/reset-time.d.ts +6 -0
  107. package/dist/src/protos/msg/proto/retry.d.ts +4 -0
  108. package/dist/src/protos/msg/proto/reverse-deck.d.ts +4 -0
  109. package/dist/src/protos/msg/proto/rock-paper-scissors.d.ts +5 -0
  110. package/dist/src/protos/msg/proto/select-battlecmd.d.ts +25 -0
  111. package/dist/src/protos/msg/proto/select-card.d.ts +17 -0
  112. package/dist/src/protos/msg/proto/select-chain.d.ts +19 -0
  113. package/dist/src/protos/msg/proto/select-counter.d.ts +17 -0
  114. package/dist/src/protos/msg/proto/select-disfield.d.ts +7 -0
  115. package/dist/src/protos/msg/proto/select-effectyn.d.ts +11 -0
  116. package/dist/src/protos/msg/proto/select-idlecmd.d.ts +37 -0
  117. package/dist/src/protos/msg/proto/select-option.d.ts +7 -0
  118. package/dist/src/protos/msg/proto/select-place.d.ts +7 -0
  119. package/dist/src/protos/msg/proto/select-position.d.ts +7 -0
  120. package/dist/src/protos/msg/proto/select-sum.d.ts +20 -0
  121. package/dist/src/protos/msg/proto/select-tribute.d.ts +18 -0
  122. package/dist/src/protos/msg/proto/select-unselect-card.d.ts +20 -0
  123. package/dist/src/protos/msg/proto/select-yesno.d.ts +6 -0
  124. package/dist/src/protos/msg/proto/set.d.ts +5 -0
  125. package/dist/src/protos/msg/proto/shuffle-deck.d.ts +5 -0
  126. package/dist/src/protos/msg/proto/shuffle-extra.d.ts +8 -0
  127. package/dist/src/protos/msg/proto/shuffle-hand.d.ts +8 -0
  128. package/dist/src/protos/msg/proto/shuffle-set-card.d.ts +17 -0
  129. package/dist/src/protos/msg/proto/sort-card.d.ts +13 -0
  130. package/dist/src/protos/msg/proto/spsummoned.d.ts +4 -0
  131. package/dist/src/protos/msg/proto/spsummoning.d.ts +9 -0
  132. package/dist/src/protos/msg/proto/start.d.ts +14 -0
  133. package/dist/src/protos/msg/proto/summoned.d.ts +4 -0
  134. package/dist/src/protos/msg/proto/summoning.d.ts +9 -0
  135. package/dist/src/protos/msg/proto/swap-grave-deck.d.ts +5 -0
  136. package/dist/src/protos/msg/proto/swap.d.ts +12 -0
  137. package/dist/src/protos/msg/proto/tag-swap.d.ts +14 -0
  138. package/dist/src/protos/msg/proto/toss-coin.d.ts +7 -0
  139. package/dist/src/protos/msg/proto/toss-dice.d.ts +7 -0
  140. package/dist/src/protos/msg/proto/update-card.d.ts +14 -0
  141. package/dist/src/protos/msg/proto/update-data.d.ts +12 -0
  142. package/dist/src/protos/msg/proto/waiting.d.ts +4 -0
  143. package/dist/src/protos/msg/proto/win.d.ts +6 -0
  144. package/dist/src/protos/msg/registry.d.ts +3 -0
  145. package/dist/src/protos/stoc/base.d.ts +17 -0
  146. package/dist/src/protos/stoc/index.d.ts +3 -0
  147. package/dist/src/protos/stoc/proto/change-side.d.ts +4 -0
  148. package/dist/src/protos/stoc/proto/chat.d.ts +10 -0
  149. package/dist/src/protos/stoc/proto/create-game.d.ts +5 -0
  150. package/dist/src/protos/stoc/proto/deck-count.d.ts +5 -0
  151. package/dist/src/protos/stoc/proto/duel-end.d.ts +4 -0
  152. package/dist/src/protos/stoc/proto/duel-start.d.ts +4 -0
  153. package/dist/src/protos/stoc/proto/error-msg.d.ts +6 -0
  154. package/dist/src/protos/stoc/proto/field-finish.d.ts +4 -0
  155. package/dist/src/protos/stoc/proto/game-msg.d.ts +9 -0
  156. package/dist/src/protos/stoc/proto/hand-result.d.ts +6 -0
  157. package/dist/src/protos/stoc/proto/hs-player-change.d.ts +5 -0
  158. package/dist/src/protos/stoc/proto/hs-player-enter.d.ts +6 -0
  159. package/dist/src/protos/stoc/proto/hs-watch-change.d.ts +5 -0
  160. package/dist/src/protos/stoc/proto/index.d.ts +24 -0
  161. package/dist/src/protos/stoc/proto/join-game.d.ts +6 -0
  162. package/dist/src/protos/stoc/proto/leave-game.d.ts +5 -0
  163. package/dist/src/protos/stoc/proto/replay.d.ts +11 -0
  164. package/dist/src/protos/stoc/proto/select-hand.d.ts +4 -0
  165. package/dist/src/protos/stoc/proto/select-tp.d.ts +4 -0
  166. package/dist/src/protos/stoc/proto/srvpro-roomlist.d.ts +18 -0
  167. package/dist/src/protos/stoc/proto/teammate-surrender.d.ts +4 -0
  168. package/dist/src/protos/stoc/proto/time-limit.d.ts +6 -0
  169. package/dist/src/protos/stoc/proto/tp-result.d.ts +4 -0
  170. package/dist/src/protos/stoc/proto/type-change.d.ts +5 -0
  171. package/dist/src/protos/stoc/proto/waiting-side.d.ts +4 -0
  172. package/dist/src/protos/stoc/registry.d.ts +3 -0
  173. package/dist/src/vendor/ocgcore-constants.d.ts +360 -0
  174. package/dist/src/vendor/script-constants.d.ts +836 -0
  175. package/index.ts +6 -0
  176. package/package.json +74 -0
  177. package/scripts/gen-constants.js +156 -0
  178. package/tsconfig.json +20 -0
@@ -0,0 +1,289 @@
1
+ # real_ip 字段改为字符串类型
2
+
3
+ ## 更新日期
4
+ 2026-02-02
5
+
6
+ ## 改动说明
7
+
8
+ ### 修改内容
9
+
10
+ `CTOS_EXTERNAL_ADDRESS` 协议的 `real_ip` 字段从 `number` 改为 `string` 类型。
11
+
12
+ **文件**: `src/protos/ctos/proto/external-address.ts`
13
+
14
+ ### 之前 ❌
15
+
16
+ ```typescript
17
+ export class YGOProCtosExternalAddress extends YGOProCtosBase {
18
+ static identifier = 0x17;
19
+ real_ip: number; // uint32_t
20
+ hostname: string;
21
+ }
22
+
23
+ // 使用方式
24
+ const ext = new YGOProCtosExternalAddress();
25
+ ext.real_ip = 0x7F000001; // 必须手动计算网络序
26
+ ```
27
+
28
+ ### 之后 ✅
29
+
30
+ ```typescript
31
+ export class YGOProCtosExternalAddress extends YGOProCtosBase {
32
+ static identifier = 0x17;
33
+ real_ip: string; // IPv4 address as string
34
+ hostname: string;
35
+ }
36
+
37
+ // 使用方式
38
+ const ext = new YGOProCtosExternalAddress();
39
+ ext.real_ip = "127.0.0.1"; // 直接使用字符串
40
+ ```
41
+
42
+ ## 优势
43
+
44
+ ### 1. 更直观的 API ✨
45
+
46
+ ```typescript
47
+ // ❌ 之前: 需要手动转换
48
+ ext.real_ip = 0x7F000001; // 这是什么 IP?
49
+
50
+ // ✅ 现在: 一目了然
51
+ ext.real_ip = "127.0.0.1";
52
+ ```
53
+
54
+ ### 2. 自动处理字节序 🔄
55
+
56
+ ```typescript
57
+ // 自动转换为网络序(大端序)
58
+ ext.real_ip = "192.168.1.1";
59
+ const payload = ext.toPayload();
60
+ // payload[0-3] = [0xC0, 0xA8, 0x01, 0x01]
61
+
62
+ // 自动从网络序转换回字符串
63
+ ext.fromPayload(payload);
64
+ console.log(ext.real_ip); // "192.168.1.1"
65
+ ```
66
+
67
+ ### 3. IPv6 映射支持 🌐
68
+
69
+ ```typescript
70
+ // 自动处理 IPv6 映射的 IPv4 地址
71
+ ext.real_ip = "::ffff:192.168.1.1";
72
+ const payload = ext.toPayload();
73
+ // 自动提取为 192.168.1.1 并编码
74
+
75
+ // 解析结果始终为标准 IPv4 格式
76
+ ext.fromPayload(payload);
77
+ console.log(ext.real_ip); // "192.168.1.1"
78
+ ```
79
+
80
+ ### 4. 错误处理 🛡️
81
+
82
+ ```typescript
83
+ // 无效的 IP 会被转换为 0.0.0.0
84
+ ext.real_ip = "invalid.ip.address";
85
+ const payload = ext.toPayload();
86
+ // payload[0-3] = [0x00, 0x00, 0x00, 0x00]
87
+ ```
88
+
89
+ ## 实现细节
90
+
91
+ ### IPv4 字符串 → uint32 (网络序)
92
+
93
+ ```typescript
94
+ private ipToUint32(ip: string): number {
95
+ // 1. 处理 IPv6 映射格式
96
+ let ipv4 = ip.startsWith('::ffff:') ? ip.substring(7) : ip;
97
+
98
+ // 2. 分割并解析
99
+ const parts = ipv4.split('.'); // ["192", "168", "1", "1"]
100
+ const bytes = parts.map(p => parseInt(p, 10)); // [192, 168, 1, 1]
101
+
102
+ // 3. 验证
103
+ if (parts.length !== 4 || bytes.some(b => isNaN(b) || b < 0 || b > 255)) {
104
+ return 0; // 无效 IP
105
+ }
106
+
107
+ // 4. 转换为网络序(大端序)
108
+ return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
109
+ }
110
+ ```
111
+
112
+ **示例**:
113
+ - `"127.0.0.1"` → `0x7F000001`
114
+ - `"192.168.1.1"` → `0xC0A80101`
115
+ - `"::ffff:10.0.0.1"` → `0x0A000001`
116
+
117
+ ### uint32 (网络序) → IPv4 字符串
118
+
119
+ ```typescript
120
+ private uint32ToIp(value: number): string {
121
+ const byte1 = (value >>> 24) & 0xff; // 无符号右移
122
+ const byte2 = (value >>> 16) & 0xff;
123
+ const byte3 = (value >>> 8) & 0xff;
124
+ const byte4 = value & 0xff;
125
+ return `${byte1}.${byte2}.${byte3}.${byte4}`;
126
+ }
127
+ ```
128
+
129
+ **示例**:
130
+ - `0x7F000001` → `"127.0.0.1"`
131
+ - `0xC0A80101` → `"192.168.1.1"`
132
+ - `0x00000000` → `"0.0.0.0"`
133
+
134
+ ## 字节序说明
135
+
136
+ ### ⚠️ 重要:网络序(大端序)
137
+
138
+ `real_ip` 使用**网络字节序(Big Endian)**,这与其他字段不同:
139
+
140
+ | 字段 | 字节序 | 示例 |
141
+ |------|--------|------|
142
+ | player_type | Little Endian | `0x0010` → `[0x10, 0x00]` |
143
+ | **real_ip** | **Big Endian** | `0x7F000001` → `[0x7F, 0x00, 0x00, 0x01]` |
144
+
145
+ 这是因为 IPv4 地址按照网络协议标准使用大端序。
146
+
147
+ ## 使用示例
148
+
149
+ ### 基本使用
150
+
151
+ ```typescript
152
+ import { YGOProCtosExternalAddress } from 'ygopro-msg-encode';
153
+
154
+ const ext = new YGOProCtosExternalAddress();
155
+ ext.real_ip = "127.0.0.1";
156
+ ext.hostname = "example.com";
157
+
158
+ // 序列化
159
+ const payload = ext.toPayload();
160
+
161
+ // 反序列化
162
+ const parsed = new YGOProCtosExternalAddress();
163
+ parsed.fromPayload(payload);
164
+ console.log(parsed.real_ip); // "127.0.0.1"
165
+ console.log(parsed.hostname); // "example.com"
166
+ ```
167
+
168
+ ### IPv6 映射
169
+
170
+ ```typescript
171
+ const ext = new YGOProCtosExternalAddress();
172
+ ext.real_ip = "::ffff:192.168.1.1"; // IPv6 映射格式
173
+
174
+ const payload = ext.toPayload();
175
+ // 自动提取为 192.168.1.1
176
+
177
+ const parsed = new YGOProCtosExternalAddress();
178
+ parsed.fromPayload(payload);
179
+ console.log(parsed.real_ip); // "192.168.1.1" (标准 IPv4)
180
+ ```
181
+
182
+ ### 私有网络地址
183
+
184
+ ```typescript
185
+ // 常见私有网络地址
186
+ ext.real_ip = "10.0.0.1"; // Class A private
187
+ ext.real_ip = "172.16.0.1"; // Class B private
188
+ ext.real_ip = "192.168.1.1"; // Class C private
189
+
190
+ // 本地回环地址
191
+ ext.real_ip = "127.0.0.1"; // localhost
192
+
193
+ // 特殊地址
194
+ ext.real_ip = "0.0.0.0"; // any/unspecified
195
+ ext.real_ip = "255.255.255.255"; // broadcast
196
+ ```
197
+
198
+ ## 测试
199
+
200
+ 测试文件 `test-chat-protocols.ts` 包含以下测试:
201
+
202
+ 1. **标准 IPv4 测试**
203
+ ```typescript
204
+ ext.real_ip = "127.0.0.1";
205
+ // 验证字节序: [0x7F, 0x00, 0x00, 0x01]
206
+ ```
207
+
208
+ 2. **IPv6 映射测试**
209
+ ```typescript
210
+ ext.real_ip = "::ffff:192.168.1.1";
211
+ // 验证提取并编码: [0xC0, 0xA8, 0x01, 0x01]
212
+ ```
213
+
214
+ 3. **往返转换测试**
215
+ ```typescript
216
+ const original = "192.168.1.1";
217
+ ext.real_ip = original;
218
+ const payload = ext.toPayload();
219
+ ext.fromPayload(payload);
220
+ assert(ext.real_ip === original);
221
+ ```
222
+
223
+ 运行测试:
224
+ ```bash
225
+ npx tsx test-chat-protocols.ts
226
+ ```
227
+
228
+ ## 迁移指南
229
+
230
+ 如果你之前使用数字格式的 `real_ip`:
231
+
232
+ ### 迁移步骤
233
+
234
+ 1. **将数字转换为字符串**
235
+ ```typescript
236
+ // 之前
237
+ ext.real_ip = 0x7F000001;
238
+
239
+ // 现在
240
+ ext.real_ip = "127.0.0.1";
241
+ ```
242
+
243
+ 2. **使用辅助函数转换(如果需要)**
244
+ ```typescript
245
+ function uint32ToIp(value: number): string {
246
+ return [
247
+ (value >>> 24) & 0xff,
248
+ (value >>> 16) & 0xff,
249
+ (value >>> 8) & 0xff,
250
+ value & 0xff
251
+ ].join('.');
252
+ }
253
+
254
+ // 转换旧代码
255
+ ext.real_ip = uint32ToIp(0x7F000001); // "127.0.0.1"
256
+ ```
257
+
258
+ ## 兼容性
259
+
260
+ ### ✅ 完全兼容
261
+
262
+ - 二进制格式完全相同(仍然是 4 字节网络序)
263
+ - 可以与旧版本互操作
264
+ - 只是 API 层面的改进
265
+
266
+ ### ⚠️ API 变更
267
+
268
+ - TypeScript 类型从 `number` 改为 `string`
269
+ - 需要更新代码中的类型注解
270
+
271
+ ## 构建结果
272
+
273
+ ```
274
+ ✅ 编译成功
275
+ ✅ 无 linter 错误
276
+ ✅ 无 TypeScript 错误
277
+ ✅ 文件大小: 145.2kb (ESM)
278
+ ```
279
+
280
+ ## 总结
281
+
282
+ 这次更新让 `real_ip` 字段更加:
283
+ - ✅ **易用**: 直接使用字符串格式
284
+ - ✅ **直观**: 一眼就能看出是什么 IP
285
+ - ✅ **智能**: 自动处理字节序转换
286
+ - ✅ **灵活**: 支持 IPv6 映射格式
287
+ - ✅ **兼容**: 二进制格式不变
288
+
289
+ 推荐所有用户迁移到新的字符串 API!
@@ -0,0 +1,342 @@
1
+ # 测试迁移总结
2
+
3
+ ## 迁移日期
4
+ 2026-02-02
5
+
6
+ ## 迁移内容
7
+
8
+ 将根目录的临时测试脚本迁移到 `tests/` 目录作为正式的 Jest 单元测试。
9
+
10
+ ### 迁移的文件
11
+
12
+ | 原文件 (根目录) | 新文件 (tests/) | 状态 |
13
+ |-----------------|----------------|------|
14
+ | `test-ctos-stoc.ts` | `ctos-stoc.spec.ts` | ✅ 已迁移并删除 |
15
+ | `test-srvpro-roomlist.ts` | `srvpro-roomlist.spec.ts` | ✅ 已迁移并删除 |
16
+ | `test-chat-protocols.ts` | `chat-protocols.spec.ts` | ✅ 已迁移并删除 |
17
+
18
+ ## 测试结果
19
+
20
+ ```
21
+ ✅ Test Suites: 7 passed, 7 total
22
+ ✅ Tests: 96 passed, 96 total
23
+ ✅ Time: 10.371 s
24
+ ```
25
+
26
+ ### 测试套件列表
27
+
28
+ 1. `sample.spec.ts` - 示例测试 ✅
29
+ 2. `binary.spec.ts` - 二进制序列化测试 ✅
30
+ 3. `msg.spec.ts` - MSG 协议测试 ✅
31
+ 4. `card-query.spec.ts` - 卡片查询测试 ✅
32
+ 5. **`ctos-stoc.spec.ts` - CTOS/STOC 协议测试** ✅ NEW
33
+ 6. **`srvpro-roomlist.spec.ts` - SRVPro 房间列表测试** ✅ NEW
34
+ 7. **`chat-protocols.spec.ts` - 可变长度字符串协议测试** ✅ NEW
35
+
36
+ ## 主要改动
37
+
38
+ ### 1. 从脚本转换为 Jest 测试
39
+
40
+ **之前** (临时脚本):
41
+ ```typescript
42
+ #!/usr/bin/env node
43
+ import { ... } from './index';
44
+
45
+ console.log('Testing...');
46
+ const test = new SomeProtocol();
47
+ // ...
48
+ console.log('Success:', result === expected);
49
+ ```
50
+
51
+ **之后** (Jest 单元测试):
52
+ ```typescript
53
+ import { ... } from '../index';
54
+
55
+ describe('Protocol Name', () => {
56
+ it('should do something', () => {
57
+ const test = new SomeProtocol();
58
+ // ...
59
+ expect(result).toBe(expected);
60
+ });
61
+ });
62
+ ```
63
+
64
+ ### 2. 添加辅助函数
65
+
66
+ 由于 CTOS/STOC 的 Base 类只处理 body 部分,添加了辅助函数来创建完整的数据包:
67
+
68
+ ```typescript
69
+ // Helper function to create full CTOS packet
70
+ function createCtosPacket(protocol: YGOProCtosBase): Uint8Array {
71
+ const body = protocol.toPayload();
72
+ const length = 1 + body.length; // identifier + body
73
+ const fullPayload = new Uint8Array(3 + body.length);
74
+ fullPayload[0] = length & 0xff;
75
+ fullPayload[1] = (length >> 8) & 0xff;
76
+ fullPayload[2] = protocol.identifier;
77
+ fullPayload.set(body, 3);
78
+ return fullPayload;
79
+ }
80
+
81
+ // Helper function to create full STOC packet
82
+ function createStocPacket(protocol: YGOProStocBase): Uint8Array {
83
+ const body = protocol.toPayload();
84
+ const length = 1 + body.length; // identifier + body
85
+ const fullPayload = new Uint8Array(3 + body.length);
86
+ fullPayload[0] = length & 0xff;
87
+ fullPayload[1] = (length >> 8) & 0xff;
88
+ fullPayload[2] = protocol.identifier;
89
+ fullPayload.set(body, 3);
90
+ return fullPayload;
91
+ }
92
+ ```
93
+
94
+ ### 3. 测试覆盖优化
95
+
96
+ 每个测试文件都扩展了测试用例,提高了代码覆盖率:
97
+
98
+ #### `ctos-stoc.spec.ts` (10 个测试)
99
+ - ✅ CTOS_PLAYER_INFO 序列化/反序列化
100
+ - ✅ CTOS_HAND_RESULT 序列化/反序列化
101
+ - ✅ CTOS_HAND_RESULT 不同值测试
102
+ - ✅ STOC_ERROR_MSG 序列化/反序列化
103
+ - ✅ STOC_ERROR_MSG padding 测试
104
+ - ✅ STOC_HAND_RESULT 序列化/反序列化
105
+ - ✅ Registry CTOS 协议识别
106
+ - ✅ Registry STOC 协议识别
107
+ - ✅ Registry 未知协议测试
108
+
109
+ #### `srvpro-roomlist.spec.ts` (6 个测试)
110
+ - ✅ SrvproRoomInfo 创建
111
+ - ✅ 空房间列表
112
+ - ✅ Waiting 状态房间
113
+ - ✅ Dueling 状态房间(含分数和 LP)
114
+ - ✅ 多个房间
115
+ - ✅ Siding 状态房间
116
+
117
+ #### `chat-protocols.spec.ts` (14 个测试)
118
+ - ✅ CTOS_CHAT 短消息
119
+ - ✅ CTOS_CHAT 长消息
120
+ - ✅ CTOS_CHAT 空消息
121
+ - ✅ CTOS_CHAT 无 null terminator 解析
122
+ - ✅ STOC_CHAT 带 player_type
123
+ - ✅ STOC_CHAT 不同 player types
124
+ - ✅ STOC_CHAT 空消息
125
+ - ✅ EXTERNAL_ADDRESS IPv4 地址
126
+ - ✅ EXTERNAL_ADDRESS IPv6 映射地址
127
+ - ✅ EXTERNAL_ADDRESS 零地址
128
+ - ✅ EXTERNAL_ADDRESS 私有网络地址
129
+ - ✅ EXTERNAL_ADDRESS 无效 IP 处理
130
+ - ✅ EXTERNAL_ADDRESS 空主机名
131
+ - ✅ 带宽优化验证(CTOS_CHAT)
132
+ - ✅ 带宽优化验证(STOC_CHAT)
133
+
134
+ ## 测试策略
135
+
136
+ ### 1. 正常流程测试
137
+ - 创建协议对象 → 设置字段 → 序列化 → 反序列化 → 验证
138
+
139
+ ### 2. 边界情况测试
140
+ - 空字符串
141
+ - 零值
142
+ - 最大值
143
+ - 无效输入
144
+
145
+ ### 3. 特殊功能测试
146
+ - IPv6 映射支持
147
+ - 可变长度优化
148
+ - Padding 处理
149
+ - Registry 识别
150
+
151
+ ### 4. 往返测试(Round-trip)
152
+ - 确保 serialize → deserialize 后数据一致
153
+
154
+ ## 运行测试
155
+
156
+ ```bash
157
+ # 运行所有测试
158
+ npm test
159
+
160
+ # 运行特定测试文件
161
+ npm test ctos-stoc
162
+ npm test srvpro-roomlist
163
+ npm test chat-protocols
164
+
165
+ # 运行并查看覆盖率
166
+ npm test -- --coverage
167
+
168
+ # 监听模式
169
+ npm test -- --watch
170
+ ```
171
+
172
+ ## 测试配置
173
+
174
+ Jest 配置在 `package.json` 中:
175
+
176
+ ```json
177
+ {
178
+ "jest": {
179
+ "moduleFileExtensions": ["js", "json", "ts"],
180
+ "rootDir": "tests",
181
+ "testRegex": ".*\\.spec\\.ts$",
182
+ "transform": {
183
+ "^.+\\.(t|j)s$": "ts-jest"
184
+ },
185
+ "testEnvironment": "node"
186
+ }
187
+ }
188
+ ```
189
+
190
+ ## 测试质量指标
191
+
192
+ ### 覆盖的协议
193
+
194
+ #### CTOS (测试覆盖: 3/19)
195
+ - ✅ CTOS_PLAYER_INFO (0x10)
196
+ - ✅ CTOS_HAND_RESULT (0x03)
197
+ - ✅ CTOS_CHAT (0x16)
198
+ - ✅ CTOS_EXTERNAL_ADDRESS (0x17)
199
+
200
+ #### STOC (测试覆盖: 4/24)
201
+ - ✅ STOC_ERROR_MSG (0x02)
202
+ - ✅ STOC_HAND_RESULT (0x05)
203
+ - ✅ STOC_CHAT (0x19)
204
+ - ✅ STOC_SRVPRO_ROOMLIST (0x31)
205
+
206
+ ### 测试类型分布
207
+ - 单元测试: 96 个
208
+ - 集成测试: 包含在单元测试中
209
+ - 端到端测试: Registry 测试
210
+
211
+ ## 关键测试用例
212
+
213
+ ### 1. 可变长度字符串
214
+ ```typescript
215
+ it('CTOS_CHAT should use variable length', () => {
216
+ const shortChat = new YGOProCtosChat();
217
+ shortChat.msg = 'Hi';
218
+ const shortBody = shortChat.toPayload();
219
+
220
+ expect(shortBody.length).toBe(6); // 2 chars * 2 + 2 null
221
+ expect(shortBody.length).toBeLessThan(512); // Much smaller!
222
+ });
223
+ ```
224
+
225
+ ### 2. IPv6 映射支持
226
+ ```typescript
227
+ it('should handle IPv6-mapped IPv4 address', () => {
228
+ const ext = new YGOProCtosExternalAddress();
229
+ ext.real_ip = '::ffff:192.168.1.1';
230
+
231
+ const body = ext.toPayload();
232
+
233
+ // Should extract IPv4 part
234
+ expect(body[0]).toBe(0xc0); // 192
235
+ expect(body[1]).toBe(0xa8); // 168
236
+ expect(body[2]).toBe(0x01); // 1
237
+ expect(body[3]).toBe(0x01); // 1
238
+
239
+ const parsed = new YGOProCtosExternalAddress();
240
+ parsed.fromPayload(body);
241
+ expect(parsed.real_ip).toBe('192.168.1.1');
242
+ });
243
+ ```
244
+
245
+ ### 3. Registry 自动识别
246
+ ```typescript
247
+ it('should correctly identify protocols', () => {
248
+ const protocol = new SomeProtocol();
249
+ // ... setup ...
250
+
251
+ const fullPayload = createPacket(protocol);
252
+ const parsed = Registry.getInstanceFromPayload(fullPayload);
253
+
254
+ expect(parsed).toBeInstanceOf(SomeProtocol);
255
+ });
256
+ ```
257
+
258
+ ## 未来测试计划
259
+
260
+ 可以继续添加的测试:
261
+
262
+ ### CTOS 协议
263
+ - [ ] CTOS_RESPONSE
264
+ - [ ] CTOS_UPDATE_DECK (需要 ygopro-deck-encode)
265
+ - [ ] CTOS_TP_RESULT
266
+ - [ ] CTOS_CREATE_GAME
267
+ - [ ] CTOS_JOIN_GAME
268
+ - [ ] CTOS_LEAVE_GAME
269
+ - [ ] CTOS_SURRENDER
270
+ - [ ] 其他协议...
271
+
272
+ ### STOC 协议
273
+ - [ ] STOC_GAME_MSG (需要 MSG 协议)
274
+ - [ ] STOC_REPLAY (需要 ygopro-yrp-encode)
275
+ - [ ] STOC_SELECT_HAND
276
+ - [ ] STOC_SELECT_TP
277
+ - [ ] STOC_JOIN_GAME
278
+ - [ ] STOC_TIME_LIMIT
279
+ - [ ] 其他协议...
280
+
281
+ ### 集成测试
282
+ - [ ] 完整的客户端-服务器通信流程
283
+ - [ ] 错误处理和恢复
284
+ - [ ] 性能测试
285
+ - [ ] 压力测试
286
+
287
+ ## 测试最佳实践
288
+
289
+ ### 1. 测试命名
290
+ ```typescript
291
+ describe('ProtocolName', () => {
292
+ it('should do something specific', () => {
293
+ // 测试代码
294
+ });
295
+ });
296
+ ```
297
+
298
+ ### 2. 断言清晰
299
+ ```typescript
300
+ // ✅ 好的断言
301
+ expect(parsed.field).toBe(expected);
302
+ expect(parsed).toBeInstanceOf(ProtocolClass);
303
+
304
+ // ❌ 避免
305
+ expect(parsed).toBeTruthy();
306
+ ```
307
+
308
+ ### 3. 测试独立性
309
+ - 每个测试应该独立
310
+ - 不依赖其他测试的执行顺序
311
+ - 使用 beforeEach/afterEach 管理共享状态
312
+
313
+ ### 4. 边界测试
314
+ - 测试最小值、最大值
315
+ - 测试空值、null、undefined
316
+ - 测试无效输入
317
+
318
+ ## 测试覆盖率
319
+
320
+ 运行覆盖率测试:
321
+
322
+ ```bash
323
+ npm test -- --coverage
324
+ ```
325
+
326
+ 当前覆盖的主要模块:
327
+ - ✅ 二进制序列化框架
328
+ - ✅ MSG 协议核心
329
+ - ✅ CTOS/STOC 协议核心
330
+ - ✅ 可变长度字符串实现
331
+ - ✅ Registry 系统
332
+ - ✅ IPv4 地址转换
333
+
334
+ ## 总结
335
+
336
+ ✅ **成功将 3 个临时测试脚本转换为正式单元测试**
337
+ ✅ **所有 96 个测试全部通过**
338
+ ✅ **测试覆盖 CTOS/STOC 的关键功能**
339
+ ✅ **使用 Jest 标准测试框架**
340
+ ✅ **删除临时测试文件,保持项目整洁**
341
+
342
+ 测试现在完全集成到项目的 CI/CD 流程中,可以随时运行验证!