模块范围
Public API(公开 API)
严重程度
Major / regression(主要问题 / 回归)
发生了什么?
ritsulib_state_divergence_20260709_001210_checksum_695_96728696.zip
复现步骤
原始 divergence 发生在 checksum #695:host 侧刚执行完 PlayCardAction card: CARD.SHADOWVERSEBYDMOD_CARD_FAIRY ... targetid: 2 后,host 和 remote 的状态校验不一致。
关键状态差异是同一张带 Exhaust, Eternal 和 Tezcatara's Ember 的 Fairy 在两端进入了不同 pile:
- host/local:Fairy 仍停在
players[0].piles.Play
- remote:Fairy 已进入
players[0].piles.Exhaust
本地日志在同一动作窗口记录了 ObjectDisposedException:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'MegaCrit.Sts2.Core.Nodes.Cards.Holders.NHandCardHolder'.
at STS2RitsuLib...NHandCardHolderFlashHandOutlinePatch.Postfix(...)
at MegaCrit.Sts2.Core.Nodes.Cards.Holders.NHandCardHolder.Flash_Patch2(...)
at shadowversebydmod.Scripts.Components.Common.PulseComponent.RefreshCost()
at shadowversebydmod.Scripts.Components.Common.PulseComponent.AfterCardPlayedLatePostfix(...)
PulseComponent.AfterCardPlayedLatePostfix 会在己方出牌后刷新手牌中 Pulse 卡的费用显示,并调用 Card.InvokeEnergyCostChanged()。当前源码中 NHandCardHolderPatch 原先在 SubscribeToEvents 的 prefix 里为“不在树上”的 hand holder 订阅 EnergyCostChanged -> NHandCardHolder.Flash。这会让离树/已释放的 hand holder 仍可能收到卡牌事件;一旦 Pulse 刷新费用,RitsuLib 的 hand-outline postfix 访问已释放的 holder,就会抛出 ObjectDisposedException。
STS2 本体 CardModel.OnPlayWrapper 中,Hook.AfterCardPlayed(...) 在移动出牌结果 pile 之前执行;该异常会中断后续 CardCmd.Exhaust(...),因此 host 侧 Fairy 停在 Play pile,而 remote 正常进入 Exhaust。
复现方式
原始复现条件来自附件:
- RitsuLib 0.4.54
- MinionLib 0.6.0
- Shadowverse Beyond Mod 1.0.3
- 双人联机,Forestcraft/Castelle 玩家打出带
Tezcatara's Ember 的 Fairy
- 出牌后 Pulse 组件刷新手牌费用显示时触发 disposed
NHandCardHolder 事件处理器
容器内不能完整模拟 Steam 双端联机,但已用 headless 测试覆盖同一关键路径:通过网络动作打出带 Exhaust 的 Fairy,并在另一个 Pulse 卡留在手牌时触发出牌后费用刷新,断言 Fairy 正确进入 Exhaust 且不留在 Play。
关键证据
/output/evidence/state-divergence-report.txt
期望行为
No response
日志 / 堆栈
RitsuLib 版本
No response
运行环境
No response
模块范围
Public API(公开 API)
严重程度
Major / regression(主要问题 / 回归)
发生了什么?
ritsulib_state_divergence_20260709_001210_checksum_695_96728696.zip
复现步骤
原始 divergence 发生在 checksum #695:host 侧刚执行完
PlayCardAction card: CARD.SHADOWVERSEBYDMOD_CARD_FAIRY ... targetid: 2后,host 和 remote 的状态校验不一致。关键状态差异是同一张带
Exhaust, Eternal和Tezcatara's Ember的 Fairy 在两端进入了不同 pile:players[0].piles.Playplayers[0].piles.Exhaust本地日志在同一动作窗口记录了
ObjectDisposedException:PulseComponent.AfterCardPlayedLatePostfix会在己方出牌后刷新手牌中 Pulse 卡的费用显示,并调用Card.InvokeEnergyCostChanged()。当前源码中NHandCardHolderPatch原先在SubscribeToEvents的 prefix 里为“不在树上”的 hand holder 订阅EnergyCostChanged -> NHandCardHolder.Flash。这会让离树/已释放的 hand holder 仍可能收到卡牌事件;一旦 Pulse 刷新费用,RitsuLib 的 hand-outline postfix 访问已释放的 holder,就会抛出ObjectDisposedException。STS2 本体
CardModel.OnPlayWrapper中,Hook.AfterCardPlayed(...)在移动出牌结果 pile 之前执行;该异常会中断后续CardCmd.Exhaust(...),因此 host 侧 Fairy 停在 Play pile,而 remote 正常进入 Exhaust。复现方式
原始复现条件来自附件:
Tezcatara's Ember的 FairyNHandCardHolder事件处理器容器内不能完整模拟 Steam 双端联机,但已用 headless 测试覆盖同一关键路径:通过网络动作打出带 Exhaust 的 Fairy,并在另一个 Pulse 卡留在手牌时触发出牌后费用刷新,断言 Fairy 正确进入 Exhaust 且不留在 Play。
关键证据
/output/evidence/state-divergence-report.txt期望行为
No response
日志 / 堆栈
RitsuLib 版本
No response
运行环境
No response