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.
- package/.eslintignore +4 -0
- package/.eslintrc.js +25 -0
- package/.prettierrc +4 -0
- package/AGENTS.md +12 -0
- package/CHANGES.md +270 -0
- package/CTOS_STOC_IMPLEMENTATION.md +279 -0
- package/FINAL_SUMMARY.md +357 -0
- package/FULL_PAYLOAD_SUMMARY.md +491 -0
- package/FULL_PAYLOAD_UPDATE.md +598 -0
- package/IMPLEMENTATION_SUMMARY.md +294 -0
- package/LICENSE +22 -0
- package/MSG_IMPLEMENTATION_SUMMARY.md +358 -0
- package/OPPONENT_VIEW_SUMMARY.md +387 -0
- package/PROJECT_COMPLETE.md +565 -0
- package/QUICK_REFERENCE.md +352 -0
- package/README.md +494 -0
- package/REAL_IP_STRING_UPDATE.md +289 -0
- package/TESTS_MIGRATION.md +342 -0
- package/VARIABLE_LENGTH_STRINGS.md +224 -0
- package/VARIABLE_LENGTH_UPDATE.md +229 -0
- package/dist/index.cjs +5131 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +5185 -0
- package/dist/index.mjs.map +7 -0
- package/dist/src/binary/binary-meta.d.ts +18 -0
- package/dist/src/binary/fill-binary-fields.d.ts +2 -0
- package/dist/src/metadata.d.ts +10 -0
- package/dist/src/proto-base/payload-base.d.ts +8 -0
- package/dist/src/proto-base/registry-base.d.ts +13 -0
- package/dist/src/protos/common/host-info.d.ts +12 -0
- package/dist/src/protos/common/index.d.ts +1 -0
- package/dist/src/protos/ctos/base.d.ts +17 -0
- package/dist/src/protos/ctos/index.d.ts +3 -0
- package/dist/src/protos/ctos/proto/chat.d.ts +9 -0
- package/dist/src/protos/ctos/proto/create-game.d.ts +8 -0
- package/dist/src/protos/ctos/proto/external-address.d.ts +12 -0
- package/dist/src/protos/ctos/proto/hand-result.d.ts +5 -0
- package/dist/src/protos/ctos/proto/hs-notready.d.ts +4 -0
- package/dist/src/protos/ctos/proto/hs-ready.d.ts +4 -0
- package/dist/src/protos/ctos/proto/hs-start.d.ts +4 -0
- package/dist/src/protos/ctos/proto/hs-toduelist.d.ts +4 -0
- package/dist/src/protos/ctos/proto/hs-toobserver.d.ts +4 -0
- package/dist/src/protos/ctos/proto/index.d.ts +19 -0
- package/dist/src/protos/ctos/proto/join-game.d.ts +7 -0
- package/dist/src/protos/ctos/proto/kick.d.ts +5 -0
- package/dist/src/protos/ctos/proto/leave-game.d.ts +4 -0
- package/dist/src/protos/ctos/proto/player-info.d.ts +5 -0
- package/dist/src/protos/ctos/proto/request-field.d.ts +4 -0
- package/dist/src/protos/ctos/proto/response.d.ts +7 -0
- package/dist/src/protos/ctos/proto/surrender.d.ts +4 -0
- package/dist/src/protos/ctos/proto/time-confirm.d.ts +4 -0
- package/dist/src/protos/ctos/proto/tp-result.d.ts +5 -0
- package/dist/src/protos/ctos/proto/update-deck.d.ts +11 -0
- package/dist/src/protos/ctos/registry.d.ts +3 -0
- package/dist/src/protos/msg/base.d.ts +9 -0
- package/dist/src/protos/msg/index.d.ts +3 -0
- package/dist/src/protos/msg/proto/add-counter.d.ts +9 -0
- package/dist/src/protos/msg/proto/announce-attrib.d.ts +7 -0
- package/dist/src/protos/msg/proto/announce-card.d.ts +7 -0
- package/dist/src/protos/msg/proto/announce-number.d.ts +7 -0
- package/dist/src/protos/msg/proto/announce-race.d.ts +7 -0
- package/dist/src/protos/msg/proto/attack-disabled.d.ts +4 -0
- package/dist/src/protos/msg/proto/attack.d.ts +12 -0
- package/dist/src/protos/msg/proto/battle.d.ts +18 -0
- package/dist/src/protos/msg/proto/become-target.d.ts +6 -0
- package/dist/src/protos/msg/proto/cancel-target.d.ts +11 -0
- package/dist/src/protos/msg/proto/card-hint.d.ts +10 -0
- package/dist/src/protos/msg/proto/card-query.d.ts +38 -0
- package/dist/src/protos/msg/proto/card-target.d.ts +11 -0
- package/dist/src/protos/msg/proto/chain-disabled.d.ts +5 -0
- package/dist/src/protos/msg/proto/chain-end.d.ts +4 -0
- package/dist/src/protos/msg/proto/chain-negated.d.ts +5 -0
- package/dist/src/protos/msg/proto/chain-solved.d.ts +5 -0
- package/dist/src/protos/msg/proto/chain-solving.d.ts +5 -0
- package/dist/src/protos/msg/proto/chained.d.ts +5 -0
- package/dist/src/protos/msg/proto/chaining.d.ts +12 -0
- package/dist/src/protos/msg/proto/confirm-cards.d.ts +16 -0
- package/dist/src/protos/msg/proto/confirm-decktop.d.ts +14 -0
- package/dist/src/protos/msg/proto/confirm-extratop.d.ts +14 -0
- package/dist/src/protos/msg/proto/damage-step-end.d.ts +4 -0
- package/dist/src/protos/msg/proto/damage-step-start.d.ts +4 -0
- package/dist/src/protos/msg/proto/damage.d.ts +6 -0
- package/dist/src/protos/msg/proto/deck-top.d.ts +8 -0
- package/dist/src/protos/msg/proto/draw.d.ts +8 -0
- package/dist/src/protos/msg/proto/equip.d.ts +12 -0
- package/dist/src/protos/msg/proto/field-disabled.d.ts +5 -0
- package/dist/src/protos/msg/proto/flipsummoned.d.ts +4 -0
- package/dist/src/protos/msg/proto/flipsummoning.d.ts +9 -0
- package/dist/src/protos/msg/proto/hand-res.d.ts +5 -0
- package/dist/src/protos/msg/proto/hint.d.ts +7 -0
- package/dist/src/protos/msg/proto/index.d.ts +85 -0
- package/dist/src/protos/msg/proto/lpupdate.d.ts +6 -0
- package/dist/src/protos/msg/proto/match-kill.d.ts +5 -0
- package/dist/src/protos/msg/proto/missed-effect.d.ts +7 -0
- package/dist/src/protos/msg/proto/move.d.ts +14 -0
- package/dist/src/protos/msg/proto/new-phase.d.ts +5 -0
- package/dist/src/protos/msg/proto/new-turn.d.ts +5 -0
- package/dist/src/protos/msg/proto/pay-lpcost.d.ts +6 -0
- package/dist/src/protos/msg/proto/player-hint.d.ts +7 -0
- package/dist/src/protos/msg/proto/pos-change.d.ts +12 -0
- package/dist/src/protos/msg/proto/random-selected.d.ts +7 -0
- package/dist/src/protos/msg/proto/recover.d.ts +6 -0
- package/dist/src/protos/msg/proto/reload-field.d.ts +36 -0
- package/dist/src/protos/msg/proto/remove-counter.d.ts +9 -0
- package/dist/src/protos/msg/proto/reset-time.d.ts +6 -0
- package/dist/src/protos/msg/proto/retry.d.ts +4 -0
- package/dist/src/protos/msg/proto/reverse-deck.d.ts +4 -0
- package/dist/src/protos/msg/proto/rock-paper-scissors.d.ts +5 -0
- package/dist/src/protos/msg/proto/select-battlecmd.d.ts +25 -0
- package/dist/src/protos/msg/proto/select-card.d.ts +17 -0
- package/dist/src/protos/msg/proto/select-chain.d.ts +19 -0
- package/dist/src/protos/msg/proto/select-counter.d.ts +17 -0
- package/dist/src/protos/msg/proto/select-disfield.d.ts +7 -0
- package/dist/src/protos/msg/proto/select-effectyn.d.ts +11 -0
- package/dist/src/protos/msg/proto/select-idlecmd.d.ts +37 -0
- package/dist/src/protos/msg/proto/select-option.d.ts +7 -0
- package/dist/src/protos/msg/proto/select-place.d.ts +7 -0
- package/dist/src/protos/msg/proto/select-position.d.ts +7 -0
- package/dist/src/protos/msg/proto/select-sum.d.ts +20 -0
- package/dist/src/protos/msg/proto/select-tribute.d.ts +18 -0
- package/dist/src/protos/msg/proto/select-unselect-card.d.ts +20 -0
- package/dist/src/protos/msg/proto/select-yesno.d.ts +6 -0
- package/dist/src/protos/msg/proto/set.d.ts +5 -0
- package/dist/src/protos/msg/proto/shuffle-deck.d.ts +5 -0
- package/dist/src/protos/msg/proto/shuffle-extra.d.ts +8 -0
- package/dist/src/protos/msg/proto/shuffle-hand.d.ts +8 -0
- package/dist/src/protos/msg/proto/shuffle-set-card.d.ts +17 -0
- package/dist/src/protos/msg/proto/sort-card.d.ts +13 -0
- package/dist/src/protos/msg/proto/spsummoned.d.ts +4 -0
- package/dist/src/protos/msg/proto/spsummoning.d.ts +9 -0
- package/dist/src/protos/msg/proto/start.d.ts +14 -0
- package/dist/src/protos/msg/proto/summoned.d.ts +4 -0
- package/dist/src/protos/msg/proto/summoning.d.ts +9 -0
- package/dist/src/protos/msg/proto/swap-grave-deck.d.ts +5 -0
- package/dist/src/protos/msg/proto/swap.d.ts +12 -0
- package/dist/src/protos/msg/proto/tag-swap.d.ts +14 -0
- package/dist/src/protos/msg/proto/toss-coin.d.ts +7 -0
- package/dist/src/protos/msg/proto/toss-dice.d.ts +7 -0
- package/dist/src/protos/msg/proto/update-card.d.ts +14 -0
- package/dist/src/protos/msg/proto/update-data.d.ts +12 -0
- package/dist/src/protos/msg/proto/waiting.d.ts +4 -0
- package/dist/src/protos/msg/proto/win.d.ts +6 -0
- package/dist/src/protos/msg/registry.d.ts +3 -0
- package/dist/src/protos/stoc/base.d.ts +17 -0
- package/dist/src/protos/stoc/index.d.ts +3 -0
- package/dist/src/protos/stoc/proto/change-side.d.ts +4 -0
- package/dist/src/protos/stoc/proto/chat.d.ts +10 -0
- package/dist/src/protos/stoc/proto/create-game.d.ts +5 -0
- package/dist/src/protos/stoc/proto/deck-count.d.ts +5 -0
- package/dist/src/protos/stoc/proto/duel-end.d.ts +4 -0
- package/dist/src/protos/stoc/proto/duel-start.d.ts +4 -0
- package/dist/src/protos/stoc/proto/error-msg.d.ts +6 -0
- package/dist/src/protos/stoc/proto/field-finish.d.ts +4 -0
- package/dist/src/protos/stoc/proto/game-msg.d.ts +9 -0
- package/dist/src/protos/stoc/proto/hand-result.d.ts +6 -0
- package/dist/src/protos/stoc/proto/hs-player-change.d.ts +5 -0
- package/dist/src/protos/stoc/proto/hs-player-enter.d.ts +6 -0
- package/dist/src/protos/stoc/proto/hs-watch-change.d.ts +5 -0
- package/dist/src/protos/stoc/proto/index.d.ts +24 -0
- package/dist/src/protos/stoc/proto/join-game.d.ts +6 -0
- package/dist/src/protos/stoc/proto/leave-game.d.ts +5 -0
- package/dist/src/protos/stoc/proto/replay.d.ts +11 -0
- package/dist/src/protos/stoc/proto/select-hand.d.ts +4 -0
- package/dist/src/protos/stoc/proto/select-tp.d.ts +4 -0
- package/dist/src/protos/stoc/proto/srvpro-roomlist.d.ts +18 -0
- package/dist/src/protos/stoc/proto/teammate-surrender.d.ts +4 -0
- package/dist/src/protos/stoc/proto/time-limit.d.ts +6 -0
- package/dist/src/protos/stoc/proto/tp-result.d.ts +4 -0
- package/dist/src/protos/stoc/proto/type-change.d.ts +5 -0
- package/dist/src/protos/stoc/proto/waiting-side.d.ts +4 -0
- package/dist/src/protos/stoc/registry.d.ts +3 -0
- package/dist/src/vendor/ocgcore-constants.d.ts +360 -0
- package/dist/src/vendor/script-constants.d.ts +836 -0
- package/index.ts +6 -0
- package/package.json +74 -0
- package/scripts/gen-constants.js +156 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# CTOS/STOC 协议实现完成总结
|
|
2
|
+
|
|
3
|
+
## 完成内容
|
|
4
|
+
|
|
5
|
+
### 1. 模块结构
|
|
6
|
+
|
|
7
|
+
按照 MSG 协议的模式,为 CTOS 和 STOC 建立了完整的模块结构:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
src/protos/
|
|
11
|
+
├── common/ # 公共数据结构
|
|
12
|
+
│ ├── host-info.ts # HostInfo 结构(20字节)
|
|
13
|
+
│ └── index.ts
|
|
14
|
+
├── ctos/ # Client to Server 协议
|
|
15
|
+
│ ├── base.ts # CTOS 基类
|
|
16
|
+
│ ├── registry.ts # CTOS 注册器
|
|
17
|
+
│ ├── index.ts
|
|
18
|
+
│ └── proto/ # 19 个 CTOS 协议实现
|
|
19
|
+
│ ├── response.ts
|
|
20
|
+
│ ├── update-deck.ts
|
|
21
|
+
│ ├── hand-result.ts
|
|
22
|
+
│ └── ... (共19个文件)
|
|
23
|
+
└── stoc/ # Server to Client 协议
|
|
24
|
+
├── base.ts # STOC 基类
|
|
25
|
+
├── registry.ts # STOC 注册器
|
|
26
|
+
├── index.ts
|
|
27
|
+
└── proto/ # 24 个 STOC 协议实现
|
|
28
|
+
├── game-msg.ts
|
|
29
|
+
├── error-msg.ts
|
|
30
|
+
├── replay.ts
|
|
31
|
+
└── ... (共24个文件)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. 协议格式处理
|
|
35
|
+
|
|
36
|
+
正确实现了 CTOS/STOC 的二进制格式:
|
|
37
|
+
- `[length 2 bytes][identifier 1 byte][body]`
|
|
38
|
+
- Base 类只处理 body 部分
|
|
39
|
+
- Registry 配置正确的 `identifierOffset: 2` 和 `dataOffset: 3`
|
|
40
|
+
|
|
41
|
+
### 3. Struct Padding 处理
|
|
42
|
+
|
|
43
|
+
正确处理了所有 C++ 结构体的 padding:
|
|
44
|
+
- `HostInfo`: 字节 9-11 padding(共 20 字节)
|
|
45
|
+
- `CTOS_JoinGame`: 字节 2-3 padding
|
|
46
|
+
- `STOC_ErrorMsg`: 字节 1-3 padding
|
|
47
|
+
- `STOC_TimeLimit`: 字节 1 padding
|
|
48
|
+
- `STOC_HS_PlayerEnter`: 实际 41 字节(workaround)
|
|
49
|
+
|
|
50
|
+
### 4. 特殊协议封装
|
|
51
|
+
|
|
52
|
+
#### CTOS_UPDATE_DECK (0x02)
|
|
53
|
+
```typescript
|
|
54
|
+
export class YGOProCtosUpdateDeck extends YGOProCtosBase {
|
|
55
|
+
static identifier = 0x2;
|
|
56
|
+
deck: YGOProDeck;
|
|
57
|
+
|
|
58
|
+
fromPayload(data: Uint8Array): this {
|
|
59
|
+
this.deck = YGOProDeck.fromUpdateDeckPayload(data);
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
toPayload(): Uint8Array {
|
|
64
|
+
return this.deck.toUpdateDeckPayload();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fromPartial(data: Partial<this>): this {
|
|
68
|
+
if (data.deck) {
|
|
69
|
+
this.deck = new YGOProDeck(data.deck);
|
|
70
|
+
}
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
copy(): this {
|
|
75
|
+
const copied = new (this.constructor as any)();
|
|
76
|
+
copied.deck = new YGOProDeck(this.deck);
|
|
77
|
+
return copied;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### STOC_REPLAY (0x17)
|
|
83
|
+
```typescript
|
|
84
|
+
export class YGOProStocReplay extends YGOProStocBase {
|
|
85
|
+
static identifier = 0x17;
|
|
86
|
+
replay: YGOProYrp;
|
|
87
|
+
|
|
88
|
+
fromPayload(data: Uint8Array): this {
|
|
89
|
+
this.replay = new YGOProYrp().fromYrp(data);
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
toPayload(): Uint8Array {
|
|
94
|
+
return this.replay.toYrp();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
fromPartial(data: Partial<this>): this {
|
|
98
|
+
if (data.replay) {
|
|
99
|
+
this.replay = new YGOProYrp(data.replay);
|
|
100
|
+
}
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
copy(): this {
|
|
105
|
+
const copied = new (this.constructor as any)();
|
|
106
|
+
copied.replay = new YGOProYrp(this.replay);
|
|
107
|
+
return copied;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### STOC_GAME_MSG (0x01)
|
|
113
|
+
```typescript
|
|
114
|
+
export class YGOProStocGameMsg extends YGOProStocBase {
|
|
115
|
+
static identifier = 0x1;
|
|
116
|
+
msg: YGOProMsgBase | undefined;
|
|
117
|
+
|
|
118
|
+
fromPayload(data: Uint8Array): this {
|
|
119
|
+
this.msg = YGOProMessages.getInstanceFromPayload(data);
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
toPayload(): Uint8Array {
|
|
124
|
+
if (!this.msg) {
|
|
125
|
+
return new Uint8Array(0);
|
|
126
|
+
}
|
|
127
|
+
return this.msg.toPayload();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fromPartial(data: Partial<this>): this {
|
|
131
|
+
if (data.msg) {
|
|
132
|
+
this.msg = data.msg.copy(); // 使用 copy()
|
|
133
|
+
}
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 5. 数组字段处理
|
|
140
|
+
|
|
141
|
+
修正了所有数组字段的写法:
|
|
142
|
+
- ❌ 错误:`@BinaryField('u16[]', 0, () => 20)`
|
|
143
|
+
- ✅ 正确:`@BinaryField('u16', 0, 20)`
|
|
144
|
+
- ✅ UTF-16:`@BinaryField('utf16', 0, 256)`
|
|
145
|
+
|
|
146
|
+
### 6. 实现的协议列表
|
|
147
|
+
|
|
148
|
+
#### CTOS 协议(19 个)
|
|
149
|
+
| ID | 协议名 | 说明 |
|
|
150
|
+
|----|--------|------|
|
|
151
|
+
| 0x01 | RESPONSE | 响应数据 |
|
|
152
|
+
| 0x02 | UPDATE_DECK | 更新卡组(ygopro-deck-encode) |
|
|
153
|
+
| 0x03 | HAND_RESULT | 猜拳结果 |
|
|
154
|
+
| 0x04 | TP_RESULT | 先后手结果 |
|
|
155
|
+
| 0x10 | PLAYER_INFO | 玩家信息 |
|
|
156
|
+
| 0x11 | CREATE_GAME | 创建房间 |
|
|
157
|
+
| 0x12 | JOIN_GAME | 加入房间 |
|
|
158
|
+
| 0x13 | LEAVE_GAME | 离开房间 |
|
|
159
|
+
| 0x14 | SURRENDER | 认输 |
|
|
160
|
+
| 0x15 | TIME_CONFIRM | 时间确认 |
|
|
161
|
+
| 0x16 | CHAT | 聊天 |
|
|
162
|
+
| 0x17 | EXTERNAL_ADDRESS | 外部地址 |
|
|
163
|
+
| 0x20 | HS_TODUELIST | 切换到决斗者 |
|
|
164
|
+
| 0x21 | HS_TOOBSERVER | 切换到观战者 |
|
|
165
|
+
| 0x22 | HS_READY | 准备 |
|
|
166
|
+
| 0x23 | HS_NOTREADY | 取消准备 |
|
|
167
|
+
| 0x24 | HS_KICK | 踢人 |
|
|
168
|
+
| 0x25 | HS_START | 开始决斗 |
|
|
169
|
+
| 0x30 | REQUEST_FIELD | 请求场地信息 |
|
|
170
|
+
|
|
171
|
+
#### STOC 协议(24 个)
|
|
172
|
+
| ID | 协议名 | 说明 |
|
|
173
|
+
|----|--------|------|
|
|
174
|
+
| 0x01 | GAME_MSG | 游戏消息(YGOProMessages) |
|
|
175
|
+
| 0x02 | ERROR_MSG | 错误消息 |
|
|
176
|
+
| 0x03 | SELECT_HAND | 选择猜拳 |
|
|
177
|
+
| 0x04 | SELECT_TP | 选择先后手 |
|
|
178
|
+
| 0x05 | HAND_RESULT | 猜拳结果 |
|
|
179
|
+
| 0x06 | TP_RESULT | 先后手结果 |
|
|
180
|
+
| 0x07 | CHANGE_SIDE | 换边 |
|
|
181
|
+
| 0x08 | WAITING_SIDE | 等待换边 |
|
|
182
|
+
| 0x09 | DECK_COUNT | 卡组数量 |
|
|
183
|
+
| 0x11 | CREATE_GAME | 创建房间 |
|
|
184
|
+
| 0x12 | JOIN_GAME | 加入房间 |
|
|
185
|
+
| 0x13 | TYPE_CHANGE | 类型变更 |
|
|
186
|
+
| 0x14 | LEAVE_GAME | 离开房间 |
|
|
187
|
+
| 0x15 | DUEL_START | 决斗开始 |
|
|
188
|
+
| 0x16 | DUEL_END | 决斗结束 |
|
|
189
|
+
| 0x17 | REPLAY | 录像(ygopro-yrp-encode) |
|
|
190
|
+
| 0x18 | TIME_LIMIT | 时间限制 |
|
|
191
|
+
| 0x19 | CHAT | 聊天 |
|
|
192
|
+
| 0x20 | HS_PLAYER_ENTER | 玩家进入 |
|
|
193
|
+
| 0x21 | HS_PLAYER_CHANGE | 玩家状态变更 |
|
|
194
|
+
| 0x22 | HS_WATCH_CHANGE | 观战者数量变更 |
|
|
195
|
+
| 0x23 | TEAMMATE_SURRENDER | 队友认输 |
|
|
196
|
+
| 0x30 | FIELD_FINISH | 场地同步完成 |
|
|
197
|
+
| 0x31 | SRVPRO_ROOMLIST | 房间列表(SRVPro 服务器) |
|
|
198
|
+
|
|
199
|
+
### 7. 导出配置
|
|
200
|
+
|
|
201
|
+
更新了主入口文件 `index.ts`:
|
|
202
|
+
```typescript
|
|
203
|
+
export * from './src/binary/binary-meta';
|
|
204
|
+
export * from './src/binary/fill-binary-fields';
|
|
205
|
+
export * from './src/protos/common';
|
|
206
|
+
export * from './src/protos/ctos';
|
|
207
|
+
export * from './src/protos/stoc';
|
|
208
|
+
export * from './src/protos/msg';
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### 8. 构建结果
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
✓ dist/index.cjs 151.9kb (之前: 10.8kb)
|
|
215
|
+
✓ dist/index.mjs 141.7kb (之前: 9.7kb)
|
|
216
|
+
✓ dist/index.d.ts 类型声明文件已生成
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 关键设计决策
|
|
220
|
+
|
|
221
|
+
### 1. Registry Options
|
|
222
|
+
```typescript
|
|
223
|
+
{
|
|
224
|
+
identifierOffset: 2, // identifier 在字节 2(跳过 2 字节长度)
|
|
225
|
+
dataOffset: 3, // body 从字节 3 开始(跳过长度 + identifier)
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### 2. fromPartial 实现策略
|
|
230
|
+
- **YGOProStocGameMsg**: 使用 `msg.copy()` 复制
|
|
231
|
+
- **YGOProCtosUpdateDeck**: 使用 `new YGOProDeck(data.deck)` 传入构造函数
|
|
232
|
+
- **YGOProStocReplay**: 使用 `new YGOProYrp(data.replay)` 传入构造函数
|
|
233
|
+
|
|
234
|
+
### 3. copy() 方法
|
|
235
|
+
为 `YGOProCtosUpdateDeck` 和 `YGOProStocReplay` 添加了 `copy()` 方法,确保正确的深拷贝。
|
|
236
|
+
|
|
237
|
+
### 4. STOC_SRVPRO_ROOMLIST 实现
|
|
238
|
+
根据 `duelclient.cpp` 实现了 SRVPro 服务器的房间列表协议:
|
|
239
|
+
```typescript
|
|
240
|
+
export class SrvproRoomInfo {
|
|
241
|
+
roomname: string; // UTF-8, 64 bytes
|
|
242
|
+
room_status: number; // 0=Waiting, 1=Dueling, 2=Siding
|
|
243
|
+
room_duel_count: number; // 决斗计数
|
|
244
|
+
room_turn_count: number; // 回合计数
|
|
245
|
+
player1: string; // 玩家1名字, UTF-8, 128 bytes
|
|
246
|
+
player1_score: number; // 玩家1分数
|
|
247
|
+
player1_lp: number; // 玩家1 LP
|
|
248
|
+
player2: string; // 玩家2名字, UTF-8, 128 bytes
|
|
249
|
+
player2_score: number; // 玩家2分数
|
|
250
|
+
player2_lp: number; // 玩家2 LP
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export class YGOProStocSrvproRoomlist {
|
|
254
|
+
count: number; // 房间数量
|
|
255
|
+
rooms: SrvproRoomInfo[]; // 房间数组
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
每个房间信息占 333 字节。
|
|
259
|
+
|
|
260
|
+
## 参考源码
|
|
261
|
+
|
|
262
|
+
实现参照了以下源码:
|
|
263
|
+
- YGOPro ocgcore: `/home/nanahira/ygo/ygopro/ocgcore`
|
|
264
|
+
- YGOPro gframe: `/home/nanahira/ygo/ygopro/gframe`
|
|
265
|
+
- 特别是 `network.h` 文件中的结构体定义
|
|
266
|
+
- `duelclient.cpp` 中的 `STOC_SRVPRO_ROOMLIST` 实现(第 413-463 行)
|
|
267
|
+
|
|
268
|
+
## 依赖关系
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
ygopro-msg-encode
|
|
272
|
+
├── ygopro-deck-encode (卡组编解码)
|
|
273
|
+
└── ygopro-yrp-encode (录像编解码)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## 测试
|
|
277
|
+
|
|
278
|
+
已创建测试脚本 `test-ctos-stoc.ts`,可以验证:
|
|
279
|
+
- 协议序列化和反序列化
|
|
280
|
+
- Registry 正确识别协议类型
|
|
281
|
+
- 数据完整性
|
|
282
|
+
|
|
283
|
+
运行测试:
|
|
284
|
+
```bash
|
|
285
|
+
npx tsx test-ctos-stoc.ts
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## 完成时间
|
|
289
|
+
|
|
290
|
+
2026-02-02
|
|
291
|
+
|
|
292
|
+
## 状态
|
|
293
|
+
|
|
294
|
+
✅ **已完成并通过构建测试**
|
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Nanahira
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# YGOPro MSG 类实现总结
|
|
2
|
+
|
|
3
|
+
## 项目概览
|
|
4
|
+
|
|
5
|
+
已完成 **84 个 MSG 类**的完整实现,涵盖 YGOPro 协议的所有主要消息类型。
|
|
6
|
+
|
|
7
|
+
## 实现的消息类型
|
|
8
|
+
|
|
9
|
+
### 基础消息(无字段或简单字段)
|
|
10
|
+
1. MSG_RETRY (1) - 重试
|
|
11
|
+
2. MSG_WAITING (3) - 等待
|
|
12
|
+
3. MSG_WIN (5) - 胜利
|
|
13
|
+
4. MSG_NEW_TURN (40) - 新回合
|
|
14
|
+
5. MSG_NEW_PHASE (41) - 新阶段
|
|
15
|
+
6. MSG_SHUFFLE_DECK (32) - 洗卡组
|
|
16
|
+
7. MSG_SWAP_GRAVE_DECK (35) - 墓地卡组交换
|
|
17
|
+
8. MSG_REVERSE_DECK (37) - 翻转卡组
|
|
18
|
+
9. MSG_CHAIN_END (74) - 连锁结束
|
|
19
|
+
10. MSG_SUMMONED (61) - 召唤完成
|
|
20
|
+
11. MSG_SPSUMMONED (63) - 特殊召唤完成
|
|
21
|
+
12. MSG_FLIPSUMMONED (65) - 翻转召唤完成
|
|
22
|
+
13. MSG_ATTACK_DISABLED (112) - 攻击禁用
|
|
23
|
+
14. MSG_DAMAGE_STEP_START (113) - 伤害步骤开始
|
|
24
|
+
15. MSG_DAMAGE_STEP_END (114) - 伤害步骤结束
|
|
25
|
+
16. MSG_HAND_RES (133) - 猜拳结果
|
|
26
|
+
17. MSG_ROCK_PAPER_SCISSORS (132) - 猜拳
|
|
27
|
+
|
|
28
|
+
### 玩家状态消息
|
|
29
|
+
18. MSG_HINT (2) - 提示
|
|
30
|
+
19. MSG_DAMAGE (91) - 伤害
|
|
31
|
+
20. MSG_RECOVER (92) - 回复
|
|
32
|
+
21. MSG_LPUPDATE (94) - LP 更新
|
|
33
|
+
22. MSG_PAY_LPCOST (100) - 支付 LP
|
|
34
|
+
23. MSG_PLAYER_HINT (165) - 玩家提示
|
|
35
|
+
24. MSG_RESET_TIME (221) - 重置时间
|
|
36
|
+
|
|
37
|
+
### 卡片状态消息
|
|
38
|
+
25. MSG_SET (54) - 盖放
|
|
39
|
+
26. MSG_POS_CHANGE (53) - 位置变更
|
|
40
|
+
27. MSG_MOVE (50) - 移动
|
|
41
|
+
28. MSG_SWAP (55) - 交换
|
|
42
|
+
29. MSG_FIELD_DISABLED (56) - 场地禁用
|
|
43
|
+
30. MSG_SUMMONING (60) - 召唤中
|
|
44
|
+
31. MSG_SPSUMMONING (62) - 特殊召唤中
|
|
45
|
+
32. MSG_FLIPSUMMONING (64) - 翻转召唤中
|
|
46
|
+
33. MSG_CARD_HINT (160) - 卡片提示
|
|
47
|
+
34. MSG_DECK_TOP (38) - 卡组顶部
|
|
48
|
+
|
|
49
|
+
### 战斗相关消息
|
|
50
|
+
35. MSG_ATTACK (110) - 攻击宣言
|
|
51
|
+
36. MSG_BATTLE (111) - 战斗伤害计算
|
|
52
|
+
|
|
53
|
+
### 连锁相关消息
|
|
54
|
+
37. MSG_CHAINING (70) - 连锁中
|
|
55
|
+
38. MSG_CHAINED (71) - 连锁完成
|
|
56
|
+
39. MSG_CHAIN_SOLVING (72) - 连锁处理中
|
|
57
|
+
40. MSG_CHAIN_SOLVED (73) - 连锁处理完成
|
|
58
|
+
41. MSG_CHAIN_NEGATED (75) - 连锁无效
|
|
59
|
+
42. MSG_CHAIN_DISABLED (76) - 连锁禁用
|
|
60
|
+
|
|
61
|
+
### 目标和装备消息
|
|
62
|
+
43. MSG_EQUIP (93) - 装备
|
|
63
|
+
44. MSG_CARD_TARGET (96) - 卡片目标
|
|
64
|
+
45. MSG_CANCEL_TARGET (97) - 取消目标
|
|
65
|
+
46. MSG_BECOME_TARGET (83) - 成为目标
|
|
66
|
+
|
|
67
|
+
### 指示物消息
|
|
68
|
+
47. MSG_ADD_COUNTER (101) - 添加指示物
|
|
69
|
+
48. MSG_REMOVE_COUNTER (102) - 移除指示物
|
|
70
|
+
|
|
71
|
+
### 随机效果消息
|
|
72
|
+
49. MSG_TOSS_COIN (130) - 投硬币
|
|
73
|
+
50. MSG_TOSS_DICE (131) - 掷骰子
|
|
74
|
+
51. MSG_RANDOM_SELECTED (81) - 随机选择
|
|
75
|
+
52. MSG_MISSED_EFFECT (120) - 错过时点
|
|
76
|
+
|
|
77
|
+
### 宣言消息
|
|
78
|
+
53. MSG_ANNOUNCE_RACE (140) - 宣言种族
|
|
79
|
+
54. MSG_ANNOUNCE_ATTRIB (141) - 宣言属性
|
|
80
|
+
55. MSG_ANNOUNCE_CARD (142) - 宣言卡片
|
|
81
|
+
56. MSG_ANNOUNCE_NUMBER (143) - 宣言数字
|
|
82
|
+
|
|
83
|
+
### 选择消息(简单)
|
|
84
|
+
57. MSG_SELECT_YESNO (13) - 是/否选择
|
|
85
|
+
58. MSG_SELECT_OPTION (14) - 选项选择
|
|
86
|
+
59. MSG_SELECT_POSITION (19) - 位置选择
|
|
87
|
+
60. MSG_SELECT_PLACE (18) - 区域选择
|
|
88
|
+
61. MSG_SELECT_DISFIELD (24) - 禁用场地选择
|
|
89
|
+
62. MSG_SELECT_EFFECTYN (12) - 效果是/否
|
|
90
|
+
|
|
91
|
+
### 选择消息(复杂)
|
|
92
|
+
63. MSG_SELECT_CARD (15) - 卡片选择
|
|
93
|
+
64. MSG_SELECT_TRIBUTE (20) - 解放选择
|
|
94
|
+
65. MSG_SELECT_COUNTER (22) - 指示物选择
|
|
95
|
+
66. MSG_SELECT_SUM (23) - 总和选择
|
|
96
|
+
67. MSG_SELECT_UNSELECT_CARD (26) - 可选/不可选卡片
|
|
97
|
+
68. MSG_SELECT_CHAIN (16) - 连锁选择
|
|
98
|
+
69. MSG_SELECT_BATTLECMD (10) - 战斗命令选择
|
|
99
|
+
70. MSG_SELECT_IDLECMD (11) - 主要阶段命令选择
|
|
100
|
+
|
|
101
|
+
### 确认和洗牌消息
|
|
102
|
+
71. MSG_CONFIRM_DECKTOP (30) - 确认卡组顶部
|
|
103
|
+
72. MSG_CONFIRM_EXTRATOP (42) - 确认额外卡组顶部
|
|
104
|
+
73. MSG_CONFIRM_CARDS (31) - 确认卡片
|
|
105
|
+
74. MSG_SHUFFLE_HAND (33) - 洗手牌
|
|
106
|
+
75. MSG_SHUFFLE_EXTRA (39) - 洗额外卡组
|
|
107
|
+
76. MSG_SHUFFLE_SET_CARD (36) - 洗覆盖卡
|
|
108
|
+
77. MSG_SORT_CARD (25) - 排序卡片
|
|
109
|
+
78. MSG_DRAW (90) - 抽卡
|
|
110
|
+
|
|
111
|
+
### TAG 决斗消息
|
|
112
|
+
79. MSG_TAG_SWAP (161) - TAG 交换
|
|
113
|
+
|
|
114
|
+
### 特殊消息
|
|
115
|
+
80. MSG_MATCH_KILL (170) - 比赛中止
|
|
116
|
+
81. MSG_START (4) - 决斗开始
|
|
117
|
+
82. MSG_UPDATE_CARD (7) - 更新卡片(完整实现)
|
|
118
|
+
83. MSG_UPDATE_DATA (6) - 更新数据(完整实现)
|
|
119
|
+
84. MSG_RELOAD_FIELD (162) - 重载场地
|
|
120
|
+
|
|
121
|
+
## CardQuery 完整查询数据类
|
|
122
|
+
|
|
123
|
+
### 标量字段
|
|
124
|
+
- code, alias, type
|
|
125
|
+
- level, rank, attribute, race
|
|
126
|
+
- attack, defense, baseAttack, baseDefense
|
|
127
|
+
- reason, owner, status
|
|
128
|
+
- lscale, rscale, link, linkMarker
|
|
129
|
+
- position
|
|
130
|
+
|
|
131
|
+
### 数组/对象字段
|
|
132
|
+
- equipCard: CardQuery_CardLocation
|
|
133
|
+
- targetCards: CardQuery_CardLocation[]
|
|
134
|
+
- overlayCards: number[] (卡片代码数组)
|
|
135
|
+
- counters: CardQuery_Counter[] (指示物数组)
|
|
136
|
+
|
|
137
|
+
### 特性
|
|
138
|
+
- 支持所有 QUERY_* 标志位
|
|
139
|
+
- 自动按标志位顺序序列化和反序列化
|
|
140
|
+
- 正确处理可变长度数组
|
|
141
|
+
- 支持空卡片(flags = 0)
|
|
142
|
+
|
|
143
|
+
## 视角控制(opponentView/teammateView)
|
|
144
|
+
|
|
145
|
+
以下消息实现了视角控制,向对方/队友隐藏敏感信息:
|
|
146
|
+
|
|
147
|
+
1. **MSG_DRAW** - 隐藏未公开的抽牌
|
|
148
|
+
2. **MSG_SHUFFLE_HAND** - 隐藏手牌内容
|
|
149
|
+
3. **MSG_SHUFFLE_EXTRA** - 隐藏未公开的额外卡组
|
|
150
|
+
4. **MSG_DECK_TOP** - 隐藏卡组顶部信息
|
|
151
|
+
5. **MSG_CONFIRM_DECKTOP** - 隐藏未公开的卡组顶部
|
|
152
|
+
6. **MSG_CONFIRM_EXTRATOP** - 隐藏未公开的额外顶部
|
|
153
|
+
7. **MSG_CONFIRM_CARDS** - 隐藏未公开的卡片
|
|
154
|
+
8. **MSG_TAG_SWAP** - TAG 交换时隐藏手牌和额外卡组
|
|
155
|
+
9. **MSG_UPDATE_CARD** - 对手视角隐藏盖放卡片的详细信息
|
|
156
|
+
10. **MSG_UPDATE_DATA** - 对手视角隐藏盖放卡片的详细信息,队友可见全部
|
|
157
|
+
|
|
158
|
+
## 小类命名规范
|
|
159
|
+
|
|
160
|
+
所有小类都使用 `{主类名}_{小类名}` 格式,并且全部导出:
|
|
161
|
+
|
|
162
|
+
### 常见小类
|
|
163
|
+
- `*_CardInfo`: 带代码的卡片信息(code + location)
|
|
164
|
+
- `*_CardLocation`: 卡片位置信息(controller + location + sequence + position)
|
|
165
|
+
- `*_SimpleCardInfo`: 简单卡片信息(code + location,7字节)
|
|
166
|
+
- `*_ActivatableInfo`: 可激活卡片信息(code + location + desc,11字节)
|
|
167
|
+
- `*_AttackableInfo`: 可攻击卡片信息(code + location + directAttack,8字节)
|
|
168
|
+
- `*_ChainInfo`: 连锁信息(13字节)
|
|
169
|
+
- `*_SetCardInfo`: 覆盖卡信息(oldLocation + newLocation)
|
|
170
|
+
- `*_CardStats`: 卡片状态(location + atk + def)
|
|
171
|
+
- `*_Counter`: 指示物信息(type + count)
|
|
172
|
+
- `*_PlayerInfo`: 玩家信息
|
|
173
|
+
- `*_ZoneCard`: 区域卡片信息
|
|
174
|
+
|
|
175
|
+
## 二进制格式
|
|
176
|
+
|
|
177
|
+
### 标准格式
|
|
178
|
+
```
|
|
179
|
+
[MSG_IDENTIFIER (1 byte)][字段数据...]
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 查询数据格式(MSG_UPDATE_CARD)
|
|
183
|
+
```
|
|
184
|
+
[MSG_IDENTIFIER][controller][location][sequence][length (4 bytes)][flags (4 bytes)][查询字段...]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 字段查询数据格式(MSG_UPDATE_DATA)
|
|
188
|
+
```
|
|
189
|
+
[MSG_IDENTIFIER][player][location]
|
|
190
|
+
[length1 (4 bytes)][flags1 (4 bytes)][查询字段1...]
|
|
191
|
+
[length2 (4 bytes)][flags2 (4 bytes)][查询字段2...]
|
|
192
|
+
...
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 文件统计
|
|
196
|
+
|
|
197
|
+
- **MSG 类文件**: 84 个
|
|
198
|
+
- **小类**: 30+ 个
|
|
199
|
+
- **测试用例**: 57 个(全部通过)
|
|
200
|
+
- **代码行数**: ~3000+ 行
|
|
201
|
+
|
|
202
|
+
## 测试覆盖
|
|
203
|
+
|
|
204
|
+
- ✅ 简单字段序列化/反序列化
|
|
205
|
+
- ✅ 动态长度数组
|
|
206
|
+
- ✅ 嵌套对象和对象数组
|
|
207
|
+
- ✅ 视角控制(opponentView/teammateView)
|
|
208
|
+
- ✅ MSG identifier 验证
|
|
209
|
+
- ✅ 查询数据完整解析
|
|
210
|
+
- ✅ 多卡片查询数据数组
|
|
211
|
+
- ✅ Registry 系统
|
|
212
|
+
|
|
213
|
+
## 使用示例
|
|
214
|
+
|
|
215
|
+
### 基础使用
|
|
216
|
+
```typescript
|
|
217
|
+
import { YGOProMsgDraw, OcgcoreCommonConstants } from 'ygopro-msg-encode';
|
|
218
|
+
|
|
219
|
+
// 创建消息
|
|
220
|
+
const msg = new YGOProMsgDraw();
|
|
221
|
+
msg.player = 0;
|
|
222
|
+
msg.count = 2;
|
|
223
|
+
msg.cards = [12345, 67890];
|
|
224
|
+
|
|
225
|
+
// 序列化
|
|
226
|
+
const binary = msg.toPayload();
|
|
227
|
+
|
|
228
|
+
// 反序列化
|
|
229
|
+
const decoded = new YGOProMsgDraw();
|
|
230
|
+
decoded.fromPayload(binary);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### 视角控制
|
|
234
|
+
```typescript
|
|
235
|
+
const msg = new YGOProMsgDraw();
|
|
236
|
+
msg.player = 0;
|
|
237
|
+
msg.count = 2;
|
|
238
|
+
msg.cards = [12345, 0x80000000 | 67890]; // 第二张卡公开
|
|
239
|
+
|
|
240
|
+
// 对方视角
|
|
241
|
+
const opponentView = msg.opponentView();
|
|
242
|
+
console.log(opponentView.cards); // [0, 0x80000000 | 67890]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### 查询数据
|
|
246
|
+
```typescript
|
|
247
|
+
import { YGOProMsgUpdateCard, CardQuery } from 'ygopro-msg-encode';
|
|
248
|
+
|
|
249
|
+
const msg = new YGOProMsgUpdateCard();
|
|
250
|
+
msg.controller = 0;
|
|
251
|
+
msg.location = 4; // MZONE
|
|
252
|
+
msg.sequence = 0;
|
|
253
|
+
|
|
254
|
+
msg.card = new CardQuery();
|
|
255
|
+
msg.card.flags = QUERY_CODE | QUERY_ATTACK | QUERY_DEFENSE;
|
|
256
|
+
msg.card.code = 89631139; // 青眼白龙
|
|
257
|
+
msg.card.attack = 3000;
|
|
258
|
+
msg.card.defense = 2500;
|
|
259
|
+
|
|
260
|
+
const binary = msg.toPayload();
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Registry 使用
|
|
264
|
+
```typescript
|
|
265
|
+
import { YGOProMessages, OcgcoreCommonConstants } from 'ygopro-msg-encode';
|
|
266
|
+
|
|
267
|
+
// 根据 identifier 获取类
|
|
268
|
+
const MsgClass = YGOProMessages.get(OcgcoreCommonConstants.MSG_WIN);
|
|
269
|
+
const msg = new MsgClass();
|
|
270
|
+
|
|
271
|
+
// 解析二进制数据
|
|
272
|
+
const identifier = data[0];
|
|
273
|
+
const MsgClass2 = YGOProMessages.get(identifier);
|
|
274
|
+
const decoded = new MsgClass2();
|
|
275
|
+
decoded.fromPayload(data);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## 技术亮点
|
|
279
|
+
|
|
280
|
+
### 1. 装饰器系统
|
|
281
|
+
使用 `@BinaryField` 装饰器定义二进制结构,支持:
|
|
282
|
+
- 固定偏移和动态偏移
|
|
283
|
+
- 固定长度和动态长度
|
|
284
|
+
- 嵌套对象和对象数组
|
|
285
|
+
- 基础类型:u8, i8, u16, i16, u32, i32, utf8, utf16
|
|
286
|
+
|
|
287
|
+
### 2. 自动序列化
|
|
288
|
+
- 自动按字段顺序序列化和反序列化
|
|
289
|
+
- 支持 Little-Endian 字节序
|
|
290
|
+
- 正确处理字符串截断和空终止符
|
|
291
|
+
|
|
292
|
+
### 3. 视角系统
|
|
293
|
+
- `opponentView()`: 对方视角(隐藏手牌、未公开信息)
|
|
294
|
+
- `teammateView()`: 队友视角(TAG 决斗)
|
|
295
|
+
- `observerView()`: 观察者视角
|
|
296
|
+
- `playerView(playerId)`: 指定玩家视角
|
|
297
|
+
|
|
298
|
+
### 4. 类型安全
|
|
299
|
+
- 完整的 TypeScript 类型定义
|
|
300
|
+
- 所有小类都导出,可独立使用
|
|
301
|
+
- 编译时类型检查
|
|
302
|
+
|
|
303
|
+
### 5. 可扩展性
|
|
304
|
+
- Registry 系统支持动态注册
|
|
305
|
+
- 易于添加新的 MSG 类型
|
|
306
|
+
- 小类可在不同 MSG 之间复用
|
|
307
|
+
|
|
308
|
+
## 参考来源
|
|
309
|
+
|
|
310
|
+
- YGOPro ocgcore: `/home/nanahira/ygo/ygopro/ocgcore`
|
|
311
|
+
- YGOPro 客户端: `/home/nanahira/ygo/ygopro/gframe`
|
|
312
|
+
- 参考实现: `/home/nanahira/ygo/koishipro-core.js`
|
|
313
|
+
|
|
314
|
+
## 视角系统实现
|
|
315
|
+
|
|
316
|
+
### playerView 方法
|
|
317
|
+
基类提供了默认实现,检查 `this['player']` 字段:
|
|
318
|
+
- 如果 `player === playerId`:返回完整数据
|
|
319
|
+
- 否则:返回 `opponentView()`
|
|
320
|
+
|
|
321
|
+
### 特殊情况
|
|
322
|
+
**MSG_UPDATE_CARD**: 字段名是 `controller` 而非 `player`,需要重写 `playerView` 方法。
|
|
323
|
+
|
|
324
|
+
### 视角类型总结
|
|
325
|
+
1. **opponentView()**: 对手视角,隐藏未公开信息
|
|
326
|
+
2. **teammateView()**: TAG 决斗队友视角
|
|
327
|
+
- 场上:可见盖放卡片
|
|
328
|
+
- 手牌:不可见非公开卡片
|
|
329
|
+
3. **observerView()**: 观察者视角,默认使用 opponentView
|
|
330
|
+
4. **playerView(playerId)**: 根据玩家 ID 自动选择视角
|
|
331
|
+
|
|
332
|
+
## 测试结果
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
Test Suites: 4 passed, 4 total
|
|
336
|
+
Tests: 66 passed, 66 total
|
|
337
|
+
Snapshots: 0 total
|
|
338
|
+
Time: ~11 seconds
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 测试覆盖
|
|
342
|
+
- ✅ 基础序列化/反序列化(31 个测试)
|
|
343
|
+
- ✅ 查询数据完整解析(11 个测试)
|
|
344
|
+
- ✅ 视角控制(24 个测试)
|
|
345
|
+
- MSG_DRAW, MSG_SHUFFLE_HAND 等的 opponentView
|
|
346
|
+
- MSG_TAG_SWAP 的 teammateView
|
|
347
|
+
- MSG_UPDATE_CARD 的视角控制
|
|
348
|
+
- 对手视角:盖放卡片隐藏
|
|
349
|
+
- 队友视角:场上盖放卡片可见,非场上盖放卡片隐藏
|
|
350
|
+
- playerView:正确使用 controller 字段
|
|
351
|
+
- MSG_UPDATE_DATA 的视角控制
|
|
352
|
+
- 对手视角:盖放卡片隐藏
|
|
353
|
+
- 队友视角:MZONE/SZONE 盖放可见,HAND 非公开不可见
|
|
354
|
+
- playerView:正确使用 player 字段
|
|
355
|
+
|
|
356
|
+
✅ 所有测试通过
|
|
357
|
+
✅ 构建成功
|
|
358
|
+
✅ 类型检查通过
|