ygopro-msg-encode 1.0.1 → 1.0.2

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 (31) hide show
  1. package/MSG_RESPONSE_GUIDE.md +578 -0
  2. package/README.md +72 -0
  3. package/dist/index.cjs +553 -82
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.mjs +546 -82
  7. package/dist/index.mjs.map +4 -4
  8. package/dist/src/protos/msg/index-response.d.ts +8 -0
  9. package/dist/src/protos/msg/index.d.ts +2 -0
  10. package/dist/src/protos/msg/proto/announce-attrib.d.ts +4 -3
  11. package/dist/src/protos/msg/proto/announce-card.d.ts +4 -3
  12. package/dist/src/protos/msg/proto/announce-number.d.ts +4 -2
  13. package/dist/src/protos/msg/proto/announce-race.d.ts +4 -3
  14. package/dist/src/protos/msg/proto/select-battlecmd.d.ts +16 -2
  15. package/dist/src/protos/msg/proto/select-card.d.ts +10 -2
  16. package/dist/src/protos/msg/proto/select-chain.d.ts +13 -3
  17. package/dist/src/protos/msg/proto/select-counter.d.ts +12 -3
  18. package/dist/src/protos/msg/proto/select-disfield.d.ts +7 -2
  19. package/dist/src/protos/msg/proto/select-effectyn.d.ts +4 -2
  20. package/dist/src/protos/msg/proto/select-idlecmd.d.ts +23 -8
  21. package/dist/src/protos/msg/proto/select-option.d.ts +4 -2
  22. package/dist/src/protos/msg/proto/select-place.d.ts +7 -2
  23. package/dist/src/protos/msg/proto/select-position.d.ts +3 -2
  24. package/dist/src/protos/msg/proto/select-sum.d.ts +9 -2
  25. package/dist/src/protos/msg/proto/select-tribute.d.ts +10 -3
  26. package/dist/src/protos/msg/proto/select-unselect-card.d.ts +10 -2
  27. package/dist/src/protos/msg/proto/select-yesno.d.ts +4 -2
  28. package/dist/src/protos/msg/proto/sort-card.d.ts +10 -2
  29. package/dist/src/protos/msg/with-response-base.d.ts +4 -0
  30. package/index.ts +2 -0
  31. package/package.json +1 -1
@@ -0,0 +1,578 @@
1
+ # MSG Response 使用指南
2
+
3
+ 本文档介绍如何使用 YGOPro MSG 协议的响应功能。
4
+
5
+ ## 概述
6
+
7
+ YGOPro 协议中有 18 种需要客户端响应的 MSG 消息。本库为这些消息提供了统一的响应生成接口。
8
+
9
+ ## 基础概念
10
+
11
+ ### YGOProMsgResponseBase
12
+
13
+ 所有需要响应的 MSG 类都继承自 `YGOProMsgResponseBase`,该基类提供两个核心方法:
14
+
15
+ - **`prepareResponse(...)`**: 根据用户选择生成响应数据
16
+ - **`defaultResponse(): Uint8Array | undefined`**: 返回"保守默认"响应(如果适用)
17
+
18
+ ## 响应方法
19
+
20
+ ### 1. 简单是/否类型
21
+
22
+ #### MSG_SELECT_EFFECTYN / MSG_SELECT_YESNO
23
+
24
+ ```typescript
25
+ import { YGOProMsgSelectEffectyn, YGOProMsgSelectYesno } from 'ygopro-msg-encode';
26
+
27
+ // 用户选择是或否
28
+ const response = msg.prepareResponse(true); // 选择"是"
29
+ const response = msg.prepareResponse(false); // 选择"否"
30
+
31
+ // 默认响应(总是"否")
32
+ const defaultResp = msg.defaultResponse(); // 等同于 prepareResponse(false)
33
+ ```
34
+
35
+ ### 2. 位置选择类型
36
+
37
+ #### MSG_SELECT_POSITION
38
+
39
+ ```typescript
40
+ import { YGOProMsgSelectPosition } from 'ygopro-msg-encode';
41
+
42
+ // 用户选择卡片位置(使用 POS_* 常量)
43
+ const response = msg.prepareResponse(0x1); // POS_FACEUP_ATTACK
44
+ ```
45
+
46
+ #### MSG_SELECT_PLACE / MSG_SELECT_DISFIELD
47
+
48
+ ```typescript
49
+ import { YGOProMsgSelectPlace, YGOProMsgSelectDisfield } from 'ygopro-msg-encode';
50
+
51
+ // 选择单个位置
52
+ const response = msg.prepareResponse([
53
+ { player: 0, location: 0x04, sequence: 2 } // LOCATION_MZONE, 序列 2
54
+ ]);
55
+
56
+ // 选择多个位置
57
+ const response = msg.prepareResponse([
58
+ { player: 0, location: 0x04, sequence: 2 },
59
+ { player: 0, location: 0x04, sequence: 3 }
60
+ ]);
61
+ ```
62
+
63
+ ### 3. 索引选择类型(支持 IndexResponse 和语义对象)
64
+
65
+ 这类消息支持两种选择方式:
66
+ 1. **直接索引选择**:使用 `IndexResponse(index)` 明确指定第几个选项
67
+ 2. **语义对象选择**:通过卡片属性(code、controller、location、sequence、desc)进行匹配
68
+
69
+ #### MSG_SELECT_BATTLECMD / MSG_SELECT_IDLECMD
70
+
71
+ ```typescript
72
+ import {
73
+ YGOProMsgSelectBattlecmd,
74
+ BattleCmdType,
75
+ YGOProMsgSelectIdlecmd,
76
+ IdleCmdType,
77
+ IndexResponse
78
+ } from 'ygopro-msg-encode';
79
+
80
+ // 方式 1: 使用 IndexResponse 直接选择第几个
81
+ const response = msg.prepareResponse(
82
+ BattleCmdType.ACTIVATE,
83
+ IndexResponse(0) // 选择第 0 个激活选项
84
+ );
85
+
86
+ // 方式 2: 使用语义对象匹配
87
+ const response = msg.prepareResponse(
88
+ BattleCmdType.ACTIVATE,
89
+ {
90
+ code: 12345678, // 卡片代码
91
+ controller: 0, // 控制者
92
+ location: 0x04, // 位置
93
+ sequence: 2, // 序列
94
+ desc: 10 // 效果描述(用于区分同一卡片的不同效果)
95
+ }
96
+ );
97
+
98
+ // 方式 3: 部分匹配(只提供部分属性)
99
+ const response = msg.prepareResponse(
100
+ IdleCmdType.SUMMON,
101
+ { code: 12345678 } // 只匹配卡片代码
102
+ );
103
+
104
+ // 特殊命令(无需选项)
105
+ const response = msg.prepareResponse(IdleCmdType.TO_BP); // 进入战斗阶段
106
+ const response = msg.prepareResponse(IdleCmdType.TO_EP); // 进入结束阶段
107
+ const response = msg.prepareResponse(IdleCmdType.SHUFFLE); // 洗手牌
108
+ ```
109
+
110
+ **命令类型枚举:**
111
+
112
+ ```typescript
113
+ // BattleCmdType
114
+ enum BattleCmdType {
115
+ ACTIVATE = 0, // 激活效果
116
+ ATTACK = 1, // 攻击
117
+ TO_M2 = 2, // 进入 M2
118
+ TO_EP = 3 // 进入结束阶段
119
+ }
120
+
121
+ // IdleCmdType
122
+ enum IdleCmdType {
123
+ SUMMON = 0, // 通常召唤
124
+ SPSUMMON = 1, // 特殊召唤
125
+ REPOS = 2, // 改变表示形式
126
+ MSET = 3, // 盖放怪兽
127
+ SSET = 4, // 盖放魔陷
128
+ ACTIVATE = 5, // 激活效果
129
+ TO_BP = 6, // 进入战斗阶段
130
+ TO_EP = 7, // 进入结束阶段
131
+ SHUFFLE = 8 // 洗手牌
132
+ }
133
+ ```
134
+
135
+ #### MSG_SELECT_OPTION
136
+
137
+ ```typescript
138
+ import { YGOProMsgSelectOption, IndexResponse } from 'ygopro-msg-encode';
139
+
140
+ // 方式 1: 使用 IndexResponse
141
+ const response = msg.prepareResponse(IndexResponse(1)); // 选择第 1 个选项
142
+
143
+ // 方式 2: 直接使用数字(简写)
144
+ const response = msg.prepareResponse(1);
145
+ ```
146
+
147
+ #### MSG_SELECT_CHAIN
148
+
149
+ ```typescript
150
+ import { YGOProMsgSelectChain, IndexResponse } from 'ygopro-msg-encode';
151
+
152
+ // 选择连锁
153
+ const response = msg.prepareResponse(IndexResponse(0));
154
+
155
+ // 或使用语义对象(包含 desc 用于区分同一卡片的不同效果)
156
+ const response = msg.prepareResponse({
157
+ code: 12345678,
158
+ controller: 0,
159
+ location: 0x04,
160
+ sequence: 2,
161
+ desc: 10
162
+ });
163
+
164
+ // 不连锁(如果允许取消)
165
+ const response = msg.prepareResponse(null);
166
+
167
+ // 默认响应(仅在无强制连锁时返回"不连锁")
168
+ const defaultResp = msg.defaultResponse();
169
+ ```
170
+
171
+ ### 4. 卡片选择类型(支持 IndexResponse 和语义对象数组)
172
+
173
+ #### MSG_SELECT_CARD
174
+
175
+ ```typescript
176
+ import { YGOProMsgSelectCard, IndexResponse } from 'ygopro-msg-encode';
177
+
178
+ // 选择卡片(使用 IndexResponse)
179
+ const response = msg.prepareResponse([
180
+ IndexResponse(0),
181
+ IndexResponse(2)
182
+ ]);
183
+
184
+ // 使用语义对象选择
185
+ const response = msg.prepareResponse([
186
+ { code: 12345678, controller: 0, location: 0x04, sequence: 0 },
187
+ { code: 87654321, location: 0x08 } // 只匹配 code 和 location
188
+ ]);
189
+
190
+ // 取消选择(如果允许)
191
+ const response = msg.prepareResponse(null);
192
+
193
+ // 默认响应(仅在 cancelable 时返回"取消")
194
+ const defaultResp = msg.defaultResponse();
195
+ ```
196
+
197
+ #### MSG_SELECT_UNSELECT_CARD
198
+
199
+ ```typescript
200
+ import { YGOProMsgSelectUnselectCard, IndexResponse } from 'ygopro-msg-encode';
201
+
202
+ // 选择一张卡片
203
+ const response = msg.prepareResponse(IndexResponse(0));
204
+
205
+ // 使用语义对象
206
+ const response = msg.prepareResponse({
207
+ code: 12345678,
208
+ controller: 0,
209
+ location: 0x04,
210
+ sequence: 1
211
+ });
212
+
213
+ // 完成选择(如果 finishable)
214
+ const response = msg.prepareResponse(null);
215
+
216
+ // 默认响应(根据 cancelable/finishable 判断)
217
+ const defaultResp = msg.defaultResponse();
218
+ ```
219
+
220
+ #### MSG_SELECT_TRIBUTE
221
+
222
+ ```typescript
223
+ import { YGOProMsgSelectTribute, IndexResponse } from 'ygopro-msg-encode';
224
+
225
+ // 选择祭品(方式同 MSG_SELECT_CARD)
226
+ const response = msg.prepareResponse([
227
+ IndexResponse(0),
228
+ IndexResponse(1)
229
+ ]);
230
+
231
+ // 取消(如果允许)
232
+ const response = msg.prepareResponse(null);
233
+ ```
234
+
235
+ #### MSG_SORT_CARD
236
+
237
+ ```typescript
238
+ import { YGOProMsgSortCard, IndexResponse } from 'ygopro-msg-encode';
239
+
240
+ // 指定排序顺序
241
+ const response = msg.prepareResponse([
242
+ IndexResponse(2),
243
+ IndexResponse(0),
244
+ IndexResponse(1)
245
+ ]);
246
+
247
+ // 使用语义对象
248
+ const response = msg.prepareResponse([
249
+ { code: 12345678 },
250
+ { code: 87654321 },
251
+ { code: 11223344 }
252
+ ]);
253
+
254
+ // 使用默认排序
255
+ const response = msg.prepareResponse(null);
256
+
257
+ // 默认响应(总是返回默认排序)
258
+ const defaultResp = msg.defaultResponse(); // 等同于 prepareResponse(null)
259
+ ```
260
+
261
+ #### MSG_SELECT_SUM
262
+
263
+ ```typescript
264
+ import { YGOProMsgSelectSum, IndexResponse } from 'ygopro-msg-encode';
265
+
266
+ // 选择等级/刻度和的卡片
267
+ const response = msg.prepareResponse([
268
+ IndexResponse(0),
269
+ IndexResponse(2),
270
+ IndexResponse(5)
271
+ ]);
272
+
273
+ // 使用语义对象
274
+ const response = msg.prepareResponse([
275
+ { code: 12345678, controller: 0, location: 0x04, sequence: 0 },
276
+ { code: 87654321, location: 0x02 }
277
+ ]);
278
+ ```
279
+
280
+ #### MSG_SELECT_COUNTER
281
+
282
+ ```typescript
283
+ import { YGOProMsgSelectCounter, IndexResponse } from 'ygopro-msg-encode';
284
+
285
+ // 为每张卡片指定移除的指示物数量
286
+ const response = msg.prepareResponse([
287
+ { card: IndexResponse(0), count: 2 },
288
+ { card: IndexResponse(1), count: 1 }
289
+ ]);
290
+
291
+ // 使用语义对象
292
+ const response = msg.prepareResponse([
293
+ { card: { code: 12345678, sequence: 0 }, count: 2 },
294
+ { card: { code: 87654321, sequence: 1 }, count: 1 }
295
+ ]);
296
+ ```
297
+
298
+ ### 5. 宣言类型
299
+
300
+ #### MSG_ANNOUNCE_RACE
301
+
302
+ ```typescript
303
+ import { YGOProMsgAnnounceRace } from 'ygopro-msg-encode';
304
+
305
+ // 宣言种族(直接传入种族值,使用 RACE_* 常量)
306
+ const response = msg.prepareResponse(0x1); // RACE_WARRIOR
307
+
308
+ // availableRaces 字段是一个位掩码,表示哪些种族可选
309
+ // 例如:availableRaces = 0x3 表示 RACE_WARRIOR (0x1) 和 RACE_SPELLCASTER (0x2) 可选
310
+ ```
311
+
312
+ #### MSG_ANNOUNCE_ATTRIB
313
+
314
+ ```typescript
315
+ import { YGOProMsgAnnounceAttrib } from 'ygopro-msg-encode';
316
+
317
+ // 宣言属性(直接传入属性值,使用 ATTRIBUTE_* 常量)
318
+ const response = msg.prepareResponse(0x1); // ATTRIBUTE_EARTH
319
+
320
+ // availableAttributes 字段是一个位掩码,表示哪些属性可选
321
+ ```
322
+
323
+ #### MSG_ANNOUNCE_CARD
324
+
325
+ ```typescript
326
+ import { YGOProMsgAnnounceCard } from 'ygopro-msg-encode';
327
+
328
+ // 宣言卡片代码(直接传入卡片代码)
329
+ const response = msg.prepareResponse(12345678);
330
+
331
+ // 注意:opcodes 字段不是可选的卡片列表,而是一个逆波兰表达式(RPN)
332
+ // 用于定义宣言条件(如种族、属性、类型等的组合条件)
333
+ // 服务器会验证宣言的卡片是否满足 opcodes 定义的条件
334
+ ```
335
+
336
+ #### MSG_ANNOUNCE_NUMBER
337
+
338
+ ```typescript
339
+ import { YGOProMsgAnnounceNumber, IndexResponse } from 'ygopro-msg-encode';
340
+
341
+ // 从可用数字中选择一个(使用 IndexResponse)
342
+ const response = msg.prepareResponse(IndexResponse(0));
343
+
344
+ // 或直接使用数字(简写,会自动查找索引)
345
+ const response = msg.prepareResponse(0);
346
+
347
+ // numbers 字段是可选的数字列表
348
+ ```
349
+
350
+ ## 语义对象匹配规则
351
+
352
+ 使用语义对象时,系统会在可选列表中查找匹配的项:
353
+
354
+ 1. **所有提供的属性都必须匹配**
355
+ 2. **未提供的属性会被忽略**
356
+ 3. **如果找不到匹配项,会抛出 `TypeError`**
357
+ 4. **如果索引超出范围,也会抛出 `TypeError`**
358
+
359
+ ```typescript
360
+ // 示例:精确匹配
361
+ const response = msg.prepareResponse(
362
+ BattleCmdType.ACTIVATE,
363
+ {
364
+ code: 12345678,
365
+ controller: 0,
366
+ location: 0x04,
367
+ sequence: 2,
368
+ desc: 10
369
+ }
370
+ );
371
+
372
+ // 示例:只匹配卡片代码
373
+ const response = msg.prepareResponse(
374
+ IdleCmdType.SUMMON,
375
+ { code: 12345678 } // 找到第一个 code 为 12345678 的卡片
376
+ );
377
+
378
+ // 示例:匹配位置
379
+ const response = msg.prepareResponse(
380
+ IdleCmdType.SPSUMMON,
381
+ { location: 0x40, sequence: 0 } // 额外卡组的第一张卡
382
+ );
383
+ ```
384
+
385
+ ## 默认响应
386
+
387
+ 某些消息提供了"保守默认"响应,通过 `defaultResponse()` 获取:
388
+
389
+ | 消息类型 | 默认响应 | 条件 |
390
+ |---------|---------|------|
391
+ | `MSG_SELECT_EFFECTYN` | `false` (否) | 总是可用 |
392
+ | `MSG_SELECT_YESNO` | `false` (否) | 总是可用 |
393
+ | `MSG_SELECT_CHAIN` | `null` (不连锁) | 无强制连锁时 |
394
+ | `MSG_SELECT_CARD` | `null` (取消) | `cancelable = 1` 时 |
395
+ | `MSG_SELECT_UNSELECT_CARD` | `null` (完成/取消) | `cancelable = 1` 或 `finishable = 1` 时 |
396
+ | `MSG_SELECT_TRIBUTE` | `null` (取消) | `cancelable = 1` 时 |
397
+ | `MSG_SORT_CARD` | `null` (默认排序) | 总是可用 |
398
+ | 其他消息 | `undefined` | 不可用 |
399
+
400
+ ```typescript
401
+ // 检查是否有默认响应
402
+ const defaultResp = msg.defaultResponse();
403
+ if (defaultResp !== undefined) {
404
+ // 使用默认响应
405
+ sendResponse(defaultResp);
406
+ } else {
407
+ // 必须由用户手动选择
408
+ promptUserForResponse(msg);
409
+ }
410
+ ```
411
+
412
+ ## 错误处理
413
+
414
+ 所有 `prepareResponse()` 方法在遇到无效输入时会抛出 `TypeError`:
415
+
416
+ ```typescript
417
+ try {
418
+ const response = msg.prepareResponse(
419
+ BattleCmdType.ACTIVATE,
420
+ IndexResponse(999) // 索引超出范围
421
+ );
422
+ } catch (e) {
423
+ if (e instanceof TypeError) {
424
+ console.error('无效的选择:', e.message);
425
+ // 可以尝试使用默认响应或提示用户重新选择
426
+ }
427
+ }
428
+
429
+ try {
430
+ const response = msg.prepareResponse(
431
+ IdleCmdType.SUMMON,
432
+ { code: 99999999 } // 找不到这个卡片
433
+ );
434
+ } catch (e) {
435
+ if (e instanceof TypeError) {
436
+ console.error('找不到匹配的卡片:', e.message);
437
+ }
438
+ }
439
+ ```
440
+
441
+ ## 常见模式
442
+
443
+ ### 模式 1: 优先使用默认响应
444
+
445
+ ```typescript
446
+ function autoRespond(msg: YGOProMsgResponseBase): Uint8Array {
447
+ const defaultResp = msg.defaultResponse();
448
+ if (defaultResp !== undefined) {
449
+ return defaultResp;
450
+ }
451
+ throw new Error('此消息需要用户手动响应');
452
+ }
453
+ ```
454
+
455
+ ### 模式 2: 智能卡片选择
456
+
457
+ ```typescript
458
+ function selectByCardCode(
459
+ msg: YGOProMsgSelectCard,
460
+ preferredCodes: number[]
461
+ ): Uint8Array {
462
+ const selections = [];
463
+
464
+ for (const code of preferredCodes) {
465
+ try {
466
+ selections.push({ code });
467
+ } catch (e) {
468
+ // 这张卡不可选,跳过
469
+ continue;
470
+ }
471
+
472
+ if (selections.length >= msg.max) {
473
+ break;
474
+ }
475
+ }
476
+
477
+ if (selections.length >= msg.min) {
478
+ return msg.prepareResponse(selections);
479
+ }
480
+
481
+ // 无法满足最小数量要求
482
+ if (msg.cancelable) {
483
+ return msg.prepareResponse(null);
484
+ }
485
+
486
+ throw new Error('无法选择足够的卡片');
487
+ }
488
+ ```
489
+
490
+ ### 模式 3: 根据效果描述选择
491
+
492
+ ```typescript
493
+ function selectEffectByDescription(
494
+ msg: YGOProMsgSelectBattlecmd,
495
+ cardCode: number,
496
+ preferredDesc: number
497
+ ): Uint8Array {
498
+ try {
499
+ return msg.prepareResponse(
500
+ BattleCmdType.ACTIVATE,
501
+ { code: cardCode, desc: preferredDesc }
502
+ );
503
+ } catch (e) {
504
+ // 该效果不可用,使用第一个可用效果
505
+ return msg.prepareResponse(
506
+ BattleCmdType.ACTIVATE,
507
+ IndexResponse(0)
508
+ );
509
+ }
510
+ }
511
+ ```
512
+
513
+ ## 注意事项
514
+
515
+ 1. **响应格式**: 所有 `prepareResponse()` 返回 `Uint8Array`,即使是单个整数值
516
+ 2. **null vs undefined**: `null` 表示"不选择/取消",`undefined` 表示"不可用"
517
+ 3. **desc 字段**: 在 `ACTIVATE` 类型的选择中,`desc` 用于区分同一卡片的不同效果
518
+ 4. **索引从 0 开始**: 使用 `IndexResponse(0)` 表示第一个选项
519
+ 5. **部分匹配**: 语义对象可以只提供部分属性,系统会找到第一个匹配项
520
+ 6. **类型安全**: 使用 TypeScript 枚举(如 `BattleCmdType`)而不是魔法数字
521
+ 7. **ANNOUNCE 类型的特殊性**:
522
+ - `MSG_ANNOUNCE_RACE` / `MSG_ANNOUNCE_ATTRIB`: 传入的是实际值(位掩码),不是索引
523
+ - `MSG_ANNOUNCE_CARD`: `opcodes` 是逆波兰表达式(条件定义),不是卡片列表;响应直接传入卡片代码
524
+ - `MSG_ANNOUNCE_NUMBER`: 既可以传入索引,也可以传入实际数字值(会自动查找)
525
+
526
+ ## 完整示例
527
+
528
+ ```typescript
529
+ import {
530
+ YGOProMsgSelectBattlecmd,
531
+ BattleCmdType,
532
+ IndexResponse
533
+ } from 'ygopro-msg-encode';
534
+
535
+ // 解析收到的 MSG
536
+ const buffer = new Uint8Array([/* ... */]);
537
+ const msg = new YGOProMsgSelectBattlecmd();
538
+ fillBinaryFields(msg, buffer);
539
+
540
+ // 根据游戏逻辑生成响应
541
+ let response: Uint8Array;
542
+
543
+ if (msg.activatableCount > 0) {
544
+ // 优先激活特定卡片的效果
545
+ try {
546
+ response = msg.prepareResponse(
547
+ BattleCmdType.ACTIVATE,
548
+ { code: 12345678, desc: 10 }
549
+ );
550
+ } catch (e) {
551
+ // 该效果不可用,激活第一个可用效果
552
+ response = msg.prepareResponse(
553
+ BattleCmdType.ACTIVATE,
554
+ IndexResponse(0)
555
+ );
556
+ }
557
+ } else if (msg.attackableCount > 0) {
558
+ // 攻击
559
+ response = msg.prepareResponse(
560
+ BattleCmdType.ATTACK,
561
+ IndexResponse(0)
562
+ );
563
+ } else if (msg.canToEp) {
564
+ // 进入结束阶段
565
+ response = msg.prepareResponse(BattleCmdType.TO_EP);
566
+ } else {
567
+ throw new Error('无可用操作');
568
+ }
569
+
570
+ // 发送响应
571
+ sendCtosResponse(response);
572
+ ```
573
+
574
+ ## 相关文件
575
+
576
+ - `src/protos/msg/with-response-base.ts` - 响应基类
577
+ - `src/protos/msg/index-response.ts` - IndexResponse 工具
578
+ - `src/protos/msg/proto/*` - 各个消息类型的实现
package/README.md CHANGED
@@ -247,8 +247,79 @@ const msgProtocol = YGOProMessages.getInstanceFromPayload(bodyPayload);
247
247
 
248
248
  All YGOPro game messages are supported. See [MSG Protocol List](./CTOS_STOC_IMPLEMENTATION.md) for details.
249
249
 
250
+ **Response Support**: 18 MSG protocols require client responses. This library provides a unified response generation API with support for index-based and semantic object selection. See [MSG Response Guide](./MSG_RESPONSE_GUIDE.md) for details.
251
+
250
252
  ## Advanced Usage
251
253
 
254
+ ### MSG Response Generation
255
+
256
+ 18 MSG protocols require client responses (e.g., card selection, yes/no, position selection). This library provides two powerful methods for generating responses:
257
+
258
+ #### prepareResponse() - Generate Response Data
259
+
260
+ ```typescript
261
+ import {
262
+ YGOProMsgSelectBattlecmd,
263
+ BattleCmdType,
264
+ IndexResponse
265
+ } from 'ygopro-msg-encode';
266
+
267
+ // Parse received MSG
268
+ const msg = new YGOProMsgSelectBattlecmd();
269
+ msg.fromPayload(msgData);
270
+
271
+ // Method 1: Index-based selection (explicit)
272
+ const response = msg.prepareResponse(
273
+ BattleCmdType.ACTIVATE,
274
+ IndexResponse(0) // Select first option
275
+ );
276
+
277
+ // Method 2: Semantic object selection (intelligent)
278
+ const response = msg.prepareResponse(
279
+ BattleCmdType.ACTIVATE,
280
+ {
281
+ code: 12345678, // Card code
282
+ location: 0x04, // LOCATION_MZONE
283
+ sequence: 2, // Sequence number
284
+ desc: 10 // Effect description (for multi-effect cards)
285
+ }
286
+ );
287
+
288
+ // Send response
289
+ sendCtosResponse(response);
290
+ ```
291
+
292
+ #### defaultResponse() - Conservative Default Behavior
293
+
294
+ Some messages support "do nothing" or conservative default responses:
295
+
296
+ ```typescript
297
+ import { YGOProMsgSelectChain } from 'ygopro-msg-encode';
298
+
299
+ const msg = new YGOProMsgSelectChain();
300
+ msg.fromPayload(msgData);
301
+
302
+ // Get default response (if available)
303
+ const defaultResp = msg.defaultResponse();
304
+ if (defaultResp !== undefined) {
305
+ // Use safe default (e.g., "no" for yes/no, cancel for optional selections)
306
+ sendCtosResponse(defaultResp);
307
+ } else {
308
+ // User input required
309
+ promptUserForResponse(msg);
310
+ }
311
+ ```
312
+
313
+ **Supported features:**
314
+ - ✅ Index-based selection with `IndexResponse(index)`
315
+ - ✅ Semantic object selection by card properties
316
+ - ✅ Partial matching (match only specified fields)
317
+ - ✅ Conservative default responses when available
318
+ - ✅ Automatic error detection (invalid index, card not found)
319
+ - ✅ Type-safe enums for command types
320
+
321
+ **See [MSG Response Guide](./MSG_RESPONSE_GUIDE.md) for complete documentation.**
322
+
252
323
  ### Special Protocols
253
324
 
254
325
  #### CTOS_UPDATE_DECK
@@ -439,6 +510,7 @@ npm run clean
439
510
 
440
511
  ## Documentation
441
512
 
513
+ - [MSG Response Guide](./MSG_RESPONSE_GUIDE.md) - **Complete guide for MSG response generation** ⭐
442
514
  - [CTOS/STOC Implementation](./CTOS_STOC_IMPLEMENTATION.md) - Detailed protocol implementation
443
515
  - [MSG Implementation](./MSG_IMPLEMENTATION_SUMMARY.md) - MSG protocol details
444
516
  - [Full Payload API](./FULL_PAYLOAD_UPDATE.md) - `toFullPayload()` / `fromFullPayload()` documentation