From e96cc3363c2829bcc112c54d75dae727208e6581 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Sat, 6 Jun 2026 20:20:34 -0400 Subject: [PATCH] refactor(battlenode): guard generated iface-impl against regen + stub visibility (M-HC-0 review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _IfaceImpl.g.cs: extend header to warn about hand-edits; tag all bare // HEADLESS-FIX lines with their milestone (M13 on GetSideLogControl ×2) so `grep HEADLESS-FIX` reliably surfaces every block before a regen. - HeadlessHandViewStub / HeadlessPlayQueueViewStub: narrow from public to internal sealed — both stubs are consumed only within SVSim.BattleEngine (via the generated partial impls); no public surface exposes the concrete type, so internal is correct and aligns with HeadlessIconAnimations. - SessionBattleEngine.SeedMulliganInfoControl: add one-line comment on the GetComponent() call explaining the shim's lazy materialisation behaviour (otherwise reads like a guaranteed NRE). Co-Authored-By: Claude Sonnet 4.6 --- SVSim.BattleEngine/Shim/Generated/_IfaceImpl.g.cs | 6 ++++-- SVSim.BattleEngine/Shim/View/HeadlessHandViewStub.cs | 2 +- SVSim.BattleEngine/Shim/View/HeadlessPlayQueueViewStub.cs | 2 +- SVSim.BattleNode/Sessions/Engine/SessionBattleEngine.cs | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/SVSim.BattleEngine/Shim/Generated/_IfaceImpl.g.cs b/SVSim.BattleEngine/Shim/Generated/_IfaceImpl.g.cs index dfb9c27..5386bb8 100644 --- a/SVSim.BattleEngine/Shim/Generated/_IfaceImpl.g.cs +++ b/SVSim.BattleEngine/Shim/Generated/_IfaceImpl.g.cs @@ -1,4 +1,6 @@ // AUTO-GENERATED (m1_iface_impl) — explicit no-op interface impls layered onto hierarchy bases. +// CONTAINS HAND-EDITS. Before any regen, grep this file for "HEADLESS-FIX" and re-apply those blocks; +// a plain regen will clobber them. namespace Wizard.Battle.View { using System; @@ -280,7 +282,7 @@ namespace Wizard.Battle.View { Vector3 global::Wizard.Battle.View.IBattlePlayerView.GetBPLabelPosition() => default!; VfxBase global::Wizard.Battle.View.IBattlePlayerView.CreateBeforeFusionVfx(BattleCardBase fusionCard, List ingredientCards) => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); VfxBase global::Wizard.Battle.View.IBattlePlayerView.ReturnActCardAfterFusion(IBattleCardView fusionCardView, bool isFusionMetamorphose = false) => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); - SideLogControl global::Wizard.Battle.View.IBattlePlayerView.GetSideLogControl(bool isSkillTargetSelect) => new SideLogControl(); // HEADLESS-FIX + SideLogControl global::Wizard.Battle.View.IBattlePlayerView.GetSideLogControl(bool isSkillTargetSelect) => new SideLogControl(); // HEADLESS-FIX (M13): generator emitted default! (null); the emit path calls GetSideLogControl and enumerates the result. VfxBase global::Wizard.Battle.View.IBattlePlayerView.SetIsNowTurnEnd(bool flg) => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); VfxBase global::Wizard.Battle.View.IBattlePlayerView.RecoveryInPlayCards() => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); VfxBase global::Wizard.Battle.View.IBattlePlayerView.RecoveryClassAndInPlayCardAttachSkillEffect() => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); @@ -387,7 +389,7 @@ namespace Wizard.Battle.View { Vector3 global::Wizard.Battle.View.IBattlePlayerView.GetBPLabelPosition() => default!; VfxBase global::Wizard.Battle.View.IBattlePlayerView.CreateBeforeFusionVfx(BattleCardBase fusionCard, List ingredientCards) => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); VfxBase global::Wizard.Battle.View.IBattlePlayerView.ReturnActCardAfterFusion(IBattleCardView fusionCardView, bool isFusionMetamorphose = false) => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); - SideLogControl global::Wizard.Battle.View.IBattlePlayerView.GetSideLogControl(bool isSkillTargetSelect) => new SideLogControl(); // HEADLESS-FIX + SideLogControl global::Wizard.Battle.View.IBattlePlayerView.GetSideLogControl(bool isSkillTargetSelect) => new SideLogControl(); // HEADLESS-FIX (M13): generator emitted default! (null); the emit path calls GetSideLogControl and enumerates the result. VfxBase global::Wizard.Battle.View.IBattlePlayerView.SetIsNowTurnEnd(bool flg) => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); VfxBase global::Wizard.Battle.View.IBattlePlayerView.RecoveryInPlayCards() => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); VfxBase global::Wizard.Battle.View.IBattlePlayerView.RecoveryClassAndInPlayCardAttachSkillEffect() => global::Wizard.Battle.View.Vfx.NullVfx.GetInstance(); diff --git a/SVSim.BattleEngine/Shim/View/HeadlessHandViewStub.cs b/SVSim.BattleEngine/Shim/View/HeadlessHandViewStub.cs index 66b177e..c85bc1a 100644 --- a/SVSim.BattleEngine/Shim/View/HeadlessHandViewStub.cs +++ b/SVSim.BattleEngine/Shim/View/HeadlessHandViewStub.cs @@ -12,7 +12,7 @@ using UnityEngine; namespace Wizard.Battle.View { - public sealed class HeadlessHandViewStub : HandViewBase + internal sealed class HeadlessHandViewStub : HandViewBase { // Shared instance the generated IBattlePlayerView.HandView getters return headless. public static readonly HeadlessHandViewStub Instance = new HeadlessHandViewStub(); diff --git a/SVSim.BattleEngine/Shim/View/HeadlessPlayQueueViewStub.cs b/SVSim.BattleEngine/Shim/View/HeadlessPlayQueueViewStub.cs index 49279dc..5244bab 100644 --- a/SVSim.BattleEngine/Shim/View/HeadlessPlayQueueViewStub.cs +++ b/SVSim.BattleEngine/Shim/View/HeadlessPlayQueueViewStub.cs @@ -16,7 +16,7 @@ using Wizard.Battle.View.Vfx; namespace Wizard.Battle.View { - public sealed class HeadlessPlayQueueViewStub : PlayQueueViewBase + internal sealed class HeadlessPlayQueueViewStub : PlayQueueViewBase { // Shared instance the generated IBattlePlayerView.PlayQueueView getters return headless. public static readonly HeadlessPlayQueueViewStub Instance = new HeadlessPlayQueueViewStub(); diff --git a/SVSim.BattleNode/Sessions/Engine/SessionBattleEngine.cs b/SVSim.BattleNode/Sessions/Engine/SessionBattleEngine.cs index 2bd82de..fa07d06 100644 --- a/SVSim.BattleNode/Sessions/Engine/SessionBattleEngine.cs +++ b/SVSim.BattleNode/Sessions/Engine/SessionBattleEngine.cs @@ -277,7 +277,7 @@ internal sealed class SessionBattleEngine // (it isn't a Component, so WireComponentFields skips it). Node seed (allowed) — pure no-op view leaves. private static void SeedMulliganInfoControl(GameObject prefab) { - var ctrl = prefab.GetComponent(); + var ctrl = prefab.GetComponent(); // Shim GameObject.GetComponent() lazily materialises a no-op component — not a real Unity scene; this is intentional and will not NRE. var partsType = typeof(MulliganInfoControl) .GetNestedType("MulliganParts", BindingFlags.NonPublic) ?? throw new InvalidOperationException("MulliganInfoControl.MulliganParts nested type not found");