ygopro-msg-encode 1.1.16 → 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
@@ -594,7 +594,9 @@ var _PayloadBase = class _PayloadBase {
594
594
  return toBinaryFields(this);
595
595
  }
596
596
  copy() {
597
- return this.fromPartial(this);
597
+ const Constructor = this.constructor;
598
+ const copied = new Constructor();
599
+ return copied.fromPartial(this);
598
600
  }
599
601
  };
600
602
  __name(_PayloadBase, "PayloadBase");
@@ -3361,19 +3363,9 @@ __decorateClass([
3361
3363
  ], _YGOProMsgConfirmDeckTop_CardInfo.prototype, "sequence", 2);
3362
3364
  var YGOProMsgConfirmDeckTop_CardInfo = _YGOProMsgConfirmDeckTop_CardInfo;
3363
3365
  var _YGOProMsgConfirmDeckTop = class _YGOProMsgConfirmDeckTop extends YGOProMsgBase {
3364
- // 对方视角可能需要隐藏卡片信息
3365
- opponentView() {
3366
- const view = this.copy();
3367
- view.cards = view.cards.map((card) => {
3368
- const c = { ...card };
3369
- if (!(c.code & 2147483648)) {
3370
- c.code = 0;
3371
- }
3372
- return c;
3373
- });
3374
- return view;
3375
- }
3376
- // confirm-decktop 使用基类的 playerView (基于 player 字段)
3366
+ // MSG_CONFIRM_DECKTOP 在 single_duel.cpp 中不做遮掩,直接发给所有人
3367
+ // ocgcore 通过 0x80000000 标记控制公开状态
3368
+ // 因此使用基类的默认实现(不遮掩)
3377
3369
  };
3378
3370
  __name(_YGOProMsgConfirmDeckTop, "YGOProMsgConfirmDeckTop");
3379
3371
  _YGOProMsgConfirmDeckTop.identifier = OcgcoreCommonConstants.MSG_CONFIRM_DECKTOP;
@@ -3406,19 +3398,9 @@ __decorateClass([
3406
3398
  ], _YGOProMsgConfirmExtraTop_CardInfo.prototype, "sequence", 2);
3407
3399
  var YGOProMsgConfirmExtraTop_CardInfo = _YGOProMsgConfirmExtraTop_CardInfo;
3408
3400
  var _YGOProMsgConfirmExtraTop = class _YGOProMsgConfirmExtraTop extends YGOProMsgBase {
3409
- // 对方视角可能需要隐藏卡片信息
3410
- opponentView() {
3411
- const view = this.copy();
3412
- view.cards = view.cards.map((card) => {
3413
- const c = { ...card };
3414
- if (!(c.code & 2147483648)) {
3415
- c.code = 0;
3416
- }
3417
- return c;
3418
- });
3419
- return view;
3420
- }
3421
- // confirm-extratop 使用基类的 playerView (基于 player 字段)
3401
+ // MSG_CONFIRM_EXTRATOP 在 single_duel.cpp 中不做遮掩,直接发给所有人
3402
+ // ocgcore 通过 0x80000000 标记控制公开状态
3403
+ // 因此使用基类的默认实现(不遮掩)
3422
3404
  };
3423
3405
  __name(_YGOProMsgConfirmExtraTop, "YGOProMsgConfirmExtraTop");
3424
3406
  _YGOProMsgConfirmExtraTop.identifier = OcgcoreCommonConstants.MSG_CONFIRM_EXTRATOP;
@@ -3474,15 +3456,9 @@ var YGOProMsgDamageStepStart = _YGOProMsgDamageStepStart;
3474
3456
 
3475
3457
  // src/protos/msg/proto/deck-top.ts
3476
3458
  var _YGOProMsgDeckTop = class _YGOProMsgDeckTop extends YGOProMsgBase {
3477
- // 对方视角可能需要隐藏卡片信息
3478
- opponentView() {
3479
- const view = this.copy();
3480
- if (!(view.code & 2147483648)) {
3481
- view.code = 0;
3482
- }
3483
- return view;
3484
- }
3485
- // deck-top 使用基类的 playerView (基于 player 字段)
3459
+ // MSG_DECK_TOP 在 single_duel.cpp 中不做遮掩,直接发给所有人
3460
+ // ocgcore 通过 0x80000000 标记控制公开状态
3461
+ // 因此使用基类的默认实现(不遮掩)
3486
3462
  };
3487
3463
  __name(_YGOProMsgDeckTop, "YGOProMsgDeckTop");
3488
3464
  _YGOProMsgDeckTop.identifier = OcgcoreCommonConstants.MSG_DECK_TOP;
@@ -5293,12 +5269,7 @@ var _YGOProMsgShuffleExtra = class _YGOProMsgShuffleExtra extends YGOProMsgBase
5293
5269
  // 对方视角需要隐藏额外卡组信息
5294
5270
  opponentView() {
5295
5271
  const view = this.copy();
5296
- view.cards = view.cards.map((card) => {
5297
- if (!(card & 2147483648)) {
5298
- return 0;
5299
- }
5300
- return card;
5301
- });
5272
+ view.cards = view.cards.map(() => 0);
5302
5273
  return view;
5303
5274
  }
5304
5275
  };
@@ -5673,7 +5644,12 @@ var _YGOProMsgTagSwap = class _YGOProMsgTagSwap extends YGOProMsgBase {
5673
5644
  // 对方和队友视角需要隐藏手牌和额外卡组信息
5674
5645
  opponentView() {
5675
5646
  const view = this.copy();
5676
- 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
+ });
5677
5653
  view.extraCards = view.extraCards.map((card) => {
5678
5654
  if (!(card & 2147483648)) {
5679
5655
  return 0;