ygopro-msg-encode 1.1.17 → 1.1.18

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.
@@ -0,0 +1,131 @@
1
+ # OpponentView 实现修复总结
2
+
3
+ ## 已修复的问题
4
+
5
+ ### 1. MSG_SHUFFLE_EXTRA (shuffle-extra.ts)
6
+
7
+ **问题**:只遮掩了非公开的卡片(没有 0x80000000 标记的)
8
+
9
+ **正确实现**:应该全部遮掩,无论是否有公开标记
10
+
11
+ **对照源码**:`single_duel.cpp` 第 1163-1179 行
12
+ ```cpp
13
+ for (int i = 0; i < count; ++i)
14
+ BufferIO::Write<int32_t>(pbuf, 0); // 全部遮掩
15
+ ```
16
+
17
+ **修复**:
18
+ ```typescript
19
+ // 修复前
20
+ view.cards = view.cards.map((card) => {
21
+ if (!(card & 0x80000000)) {
22
+ return 0;
23
+ }
24
+ return card;
25
+ });
26
+
27
+ // 修复后
28
+ view.cards = view.cards.map(() => 0); // 全部遮掩
29
+ ```
30
+
31
+ ---
32
+
33
+ ### 2. MSG_TAG_SWAP (tag-swap.ts)
34
+
35
+ **问题**:`handCards` 全部遮掩,但应该只遮掩非公开的(类似 `extraCards`)
36
+
37
+ **正确实现**:手牌和额外卡组都应该只遮掩没有 0x80000000 标记的卡片
38
+
39
+ **对照源码**:`tag_duel.cpp` 第 1932-1960 行
40
+ ```cpp
41
+ // 对手牌的处理
42
+ for (int i = 0; i < hcount; ++i) {
43
+ if(!(pbufw[3] & 0x80)) // 检查 0x80000000
44
+ BufferIO::Write<int32_t>(pbufw, 0); // 非公开的遮掩
45
+ else
46
+ pbufw += 4; // 公开的保留
47
+ }
48
+ // 对额外卡组的处理(同样逻辑)
49
+ for (int i = 0; i < ecount; ++i) {
50
+ if(!(pbufw[3] & 0x80))
51
+ BufferIO::Write<int32_t>(pbufw, 0);
52
+ else
53
+ pbufw += 4;
54
+ }
55
+ ```
56
+
57
+ **修复**:
58
+ ```typescript
59
+ // 修复前
60
+ view.handCards = view.handCards.map(() => 0); // 全部遮掩
61
+
62
+ // 修复后
63
+ view.handCards = view.handCards.map((card) => {
64
+ if (!(card & 0x80000000)) {
65
+ return 0; // 只遮掩非公开的
66
+ }
67
+ return card; // 保留公开的
68
+ });
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 其他修复
74
+
75
+ ### 3. MSG_CONFIRM_DECKTOP / MSG_CONFIRM_EXTRATOP / MSG_DECK_TOP
76
+
77
+ **问题**:项目中实现了 `opponentView` 遮掩逻辑,但 YGOPro 源码中这些消息不做遮掩
78
+
79
+ **正确实现**:移除 `opponentView` 方法,使用基类的默认实现(不遮掩)
80
+
81
+ **对照源码**:`single_duel.cpp` 第 1089-1224 行
82
+
83
+ 这些消息在源码中**直接发给所有人,不做遮掩**:
84
+ ```cpp
85
+ // MSG_CONFIRM_DECKTOP
86
+ NetServer::SendBufferToPlayer(players[0], STOC_GAME_MSG, offset, pbuf - offset);
87
+ NetServer::ReSendToPlayer(players[1]); // 直接转发,无遮掩
88
+
89
+ // MSG_CONFIRM_EXTRATOP (相同处理)
90
+ // MSG_DECK_TOP (相同处理)
91
+ ```
92
+
93
+ **原因**:ocgcore 在生成这些消息时,已经通过 `0x80000000` 标记控制了哪些内容是公开的。网络层直接转发,不需要额外遮掩。
94
+
95
+ **修复**:移除了这些消息的 `opponentView` 方法,使用基类的默认实现(直接返回 copy)。
96
+
97
+ ---
98
+
99
+ ## 0x80000000 标记的语义
100
+
101
+ 在 YGOPro 中,`0x80000000` 标记用于表示"公开"状态:
102
+
103
+ - **有标记**:这张卡是公开的,所有人都能看到
104
+ - **无标记**:这张卡是非公开的,需要根据玩家身份决定是否遮掩
105
+
106
+ **源码示例**(ocgcore/field.cpp):
107
+ ```cpp
108
+ pduel->write_buffer32(pcard->data.code | (pcard->is_position(POS_FACEUP) ? 0x80000000 : 0));
109
+ ```
110
+
111
+ ---
112
+
113
+ ## 验证方法
114
+
115
+ 对照 YGOPro 源代码:
116
+ - **单人决斗**:`/home/nanahira/ygo/ygopro/gframe/single_duel.cpp`
117
+ - **TAG 决斗**:`/home/nanahira/ygo/ygopro/gframe/tag_duel.cpp`
118
+ - **核心逻辑**:`/home/nanahira/ygo/ygopro/ocgcore/`
119
+
120
+ 关键函数:
121
+ - `RefreshHand()`: 刷新手牌
122
+ - `RefreshMzone()`: 刷新怪兽区
123
+ - `RefreshSzone()`: 刷新魔陷区
124
+ - `RefreshExtra()`: 刷新额外卡组
125
+ - `RefreshSingle()`: 刷新单张卡片
126
+
127
+ ---
128
+
129
+ ## 修复日期
130
+
131
+ 2026-02-14
package/dist/index.cjs CHANGED
@@ -3363,19 +3363,9 @@ __decorateClass([
3363
3363
  ], _YGOProMsgConfirmDeckTop_CardInfo.prototype, "sequence", 2);
3364
3364
  var YGOProMsgConfirmDeckTop_CardInfo = _YGOProMsgConfirmDeckTop_CardInfo;
3365
3365
  var _YGOProMsgConfirmDeckTop = class _YGOProMsgConfirmDeckTop extends YGOProMsgBase {
3366
- // 对方视角可能需要隐藏卡片信息
3367
- opponentView() {
3368
- const view = this.copy();
3369
- view.cards = view.cards.map((card) => {
3370
- const c = { ...card };
3371
- if (!(c.code & 2147483648)) {
3372
- c.code = 0;
3373
- }
3374
- return c;
3375
- });
3376
- return view;
3377
- }
3378
- // confirm-decktop 使用基类的 playerView (基于 player 字段)
3366
+ // MSG_CONFIRM_DECKTOP 在 single_duel.cpp 中不做遮掩,直接发给所有人
3367
+ // ocgcore 通过 0x80000000 标记控制公开状态
3368
+ // 因此使用基类的默认实现(不遮掩)
3379
3369
  };
3380
3370
  __name(_YGOProMsgConfirmDeckTop, "YGOProMsgConfirmDeckTop");
3381
3371
  _YGOProMsgConfirmDeckTop.identifier = OcgcoreCommonConstants.MSG_CONFIRM_DECKTOP;
@@ -3408,19 +3398,9 @@ __decorateClass([
3408
3398
  ], _YGOProMsgConfirmExtraTop_CardInfo.prototype, "sequence", 2);
3409
3399
  var YGOProMsgConfirmExtraTop_CardInfo = _YGOProMsgConfirmExtraTop_CardInfo;
3410
3400
  var _YGOProMsgConfirmExtraTop = class _YGOProMsgConfirmExtraTop extends YGOProMsgBase {
3411
- // 对方视角可能需要隐藏卡片信息
3412
- opponentView() {
3413
- const view = this.copy();
3414
- view.cards = view.cards.map((card) => {
3415
- const c = { ...card };
3416
- if (!(c.code & 2147483648)) {
3417
- c.code = 0;
3418
- }
3419
- return c;
3420
- });
3421
- return view;
3422
- }
3423
- // confirm-extratop 使用基类的 playerView (基于 player 字段)
3401
+ // MSG_CONFIRM_EXTRATOP 在 single_duel.cpp 中不做遮掩,直接发给所有人
3402
+ // ocgcore 通过 0x80000000 标记控制公开状态
3403
+ // 因此使用基类的默认实现(不遮掩)
3424
3404
  };
3425
3405
  __name(_YGOProMsgConfirmExtraTop, "YGOProMsgConfirmExtraTop");
3426
3406
  _YGOProMsgConfirmExtraTop.identifier = OcgcoreCommonConstants.MSG_CONFIRM_EXTRATOP;
@@ -3476,15 +3456,9 @@ var YGOProMsgDamageStepStart = _YGOProMsgDamageStepStart;
3476
3456
 
3477
3457
  // src/protos/msg/proto/deck-top.ts
3478
3458
  var _YGOProMsgDeckTop = class _YGOProMsgDeckTop extends YGOProMsgBase {
3479
- // 对方视角可能需要隐藏卡片信息
3480
- opponentView() {
3481
- const view = this.copy();
3482
- if (!(view.code & 2147483648)) {
3483
- view.code = 0;
3484
- }
3485
- return view;
3486
- }
3487
- // deck-top 使用基类的 playerView (基于 player 字段)
3459
+ // MSG_DECK_TOP 在 single_duel.cpp 中不做遮掩,直接发给所有人
3460
+ // ocgcore 通过 0x80000000 标记控制公开状态
3461
+ // 因此使用基类的默认实现(不遮掩)
3488
3462
  };
3489
3463
  __name(_YGOProMsgDeckTop, "YGOProMsgDeckTop");
3490
3464
  _YGOProMsgDeckTop.identifier = OcgcoreCommonConstants.MSG_DECK_TOP;
@@ -5295,12 +5269,7 @@ var _YGOProMsgShuffleExtra = class _YGOProMsgShuffleExtra extends YGOProMsgBase
5295
5269
  // 对方视角需要隐藏额外卡组信息
5296
5270
  opponentView() {
5297
5271
  const view = this.copy();
5298
- view.cards = view.cards.map((card) => {
5299
- if (!(card & 2147483648)) {
5300
- return 0;
5301
- }
5302
- return card;
5303
- });
5272
+ view.cards = view.cards.map(() => 0);
5304
5273
  return view;
5305
5274
  }
5306
5275
  };
@@ -5675,7 +5644,12 @@ var _YGOProMsgTagSwap = class _YGOProMsgTagSwap extends YGOProMsgBase {
5675
5644
  // 对方和队友视角需要隐藏手牌和额外卡组信息
5676
5645
  opponentView() {
5677
5646
  const view = this.copy();
5678
- view.handCards = view.handCards.map(() => 0);
5647
+ view.handCards = view.handCards.map((card) => {
5648
+ if (!(card & 2147483648)) {
5649
+ return 0;
5650
+ }
5651
+ return card;
5652
+ });
5679
5653
  view.extraCards = view.extraCards.map((card) => {
5680
5654
  if (!(card & 2147483648)) {
5681
5655
  return 0;