99using Newtonsoft . Json ;
1010using PlasmaShared ;
1111using Ratings ;
12- using Z . EntityFramework . Plus ;
1312using ZkData ;
1413using ZkLobbyServer ;
1514
@@ -778,14 +777,28 @@ private sealed class OptionSnapshot
778777 public HashSet < int > DefenderFactionIds ; // DefendCollect only
779778 }
780779
781- private List < OptionSnapshot > ComputeOptionSnapshots ( PwPhase phase )
780+ /// <summary>
781+ /// Viewer-invariant data for the whole lobby fan-out: the per-option snapshots plus the aggregate
782+ /// attacker/defender faction shortcut lists that go into the command header. Computed once per
783+ /// UpdateLobby tick.
784+ /// </summary>
785+ private sealed class LobbySnapshot
782786 {
783- var result = new List < OptionSnapshot > ( ) ;
787+ public List < OptionSnapshot > Options ;
788+ public List < string > AttackerFactionShortcuts ;
789+ public List < string > DefenderFactionShortcuts ;
790+ }
791+
792+ private LobbySnapshot ComputeLobbySnapshot ( PwPhase phase )
793+ {
794+ var options = new List < OptionSnapshot > ( ) ;
795+ var defenderShortcuts = new HashSet < string > ( ) ;
796+
784797 if ( phase == PwPhase . AttackCollect )
785798 {
786799 foreach ( var opt in AttackOptions )
787800 {
788- result . Add ( new OptionSnapshot
801+ options . Add ( new OptionSnapshot
789802 {
790803 PlanetId = opt . PlanetID ,
791804 AttackerFactionId = opt . AttackerFactionID ,
@@ -812,7 +825,13 @@ private List<OptionSnapshot> ComputeOptionSnapshots(PwPhase phase)
812825 {
813826 var atkAvg = AvgTopNWhr ( squad . Attackers , squad . TeamSize ) ;
814827 int ? defAvg = squad . DefenderVotes . Count > 0 ? ( int ? ) AvgTopNWhr ( squad . DefenderVotes , squad . TeamSize ) : null ;
815- result . Add ( new OptionSnapshot
828+ var defenderFactionIds = GetDefendingFactions ( squad ) . Select ( f => f . FactionID ) . ToHashSet ( ) ;
829+ foreach ( var fid in defenderFactionIds )
830+ {
831+ var sc = GetFactionShortcut ( fid ) ;
832+ if ( sc != null ) defenderShortcuts . Add ( sc ) ;
833+ }
834+ options . Add ( new OptionSnapshot
816835 {
817836 PlanetId = squad . PlanetID ,
818837 AttackerFactionId = squad . AttackerFactionID ,
@@ -829,42 +848,44 @@ private List<OptionSnapshot> ComputeOptionSnapshots(PwPhase phase)
829848 WinChance = ComputeWinChance ( atkAvg , defAvg ) ,
830849 AttackerNames = new HashSet < string > ( squad . Attackers ) ,
831850 DefenderNames = new HashSet < string > ( squad . DefenderVotes ) ,
832- DefenderFactionIds = GetDefendingFactions ( squad ) . Select ( f => f . FactionID ) . ToHashSet ( ) ,
851+ DefenderFactionIds = defenderFactionIds ,
833852 } ) ;
834853 }
835854 }
836- return result ;
855+
856+ return new LobbySnapshot
857+ {
858+ Options = options ,
859+ AttackerFactionShortcuts = options
860+ . Select ( s => s . AttackerFactionShortcut )
861+ . Where ( x => ! string . IsNullOrEmpty ( x ) )
862+ . Distinct ( )
863+ . ToList ( ) ,
864+ DefenderFactionShortcuts = defenderShortcuts . ToList ( ) ,
865+ } ;
837866 }
838867
839868 public PwMatchCommand GenerateLobbyCommand ( string playerName = null , string playerFaction = null )
840869 {
841870 if ( MiscVar . PlanetWarsMode != PlanetWarsModes . Running )
842871 return new PwMatchCommand ( PwMatchCommand . ModeType . Clear ) ;
843- return StampLobbyCommand ( ComputeOptionSnapshots ( Phase ) , Phase , playerName , playerFaction ) ;
872+ return StampLobbyCommand ( ComputeLobbySnapshot ( Phase ) , Phase , playerName , playerFaction ) ;
844873 }
845874
846- private PwMatchCommand StampLobbyCommand ( List < OptionSnapshot > snapshots , PwPhase phase , string playerName , string playerFaction )
875+ private PwMatchCommand StampLobbyCommand ( LobbySnapshot snapshot , PwPhase phase , string playerName , string playerFaction )
847876 {
848877 try
849878 {
850879 int ? playerFactionId = null ;
851880 if ( playerFaction != null )
852881 playerFactionId = factions . FirstOrDefault ( f => f . Shortcut == playerFaction ) ? . FactionID ;
853882
854- // Distinct attacker factions across all options — populated from the shared snapshot so it
855- // matches the per-option AttackerFaction data the client sees in both phases.
856- var attackerFactionShortcuts = snapshots
857- . Select ( s => s . AttackerFactionShortcut )
858- . Where ( x => ! string . IsNullOrEmpty ( x ) )
859- . Distinct ( )
860- . ToList ( ) ;
861-
862883 if ( phase == PwPhase . AttackCollect )
863884 {
864885 // All factions' options are shown to every viewer (parity with pre-parallel-turn UX, where
865886 // everyone could see what the current attacker was planning). CanSelectForBattle gates the
866887 // click: a player can only join options for their own faction.
867- var options = snapshots . Select ( s => new PwMatchCommand . VoteOption
888+ var options = snapshot . Options . Select ( s => new PwMatchCommand . VoteOption
868889 {
869890 PlanetID = s . PlanetId ,
870891 PlanetName = s . PlanetName ,
@@ -889,18 +910,16 @@ private PwMatchCommand StampLobbyCommand(List<OptionSnapshot> snapshots, PwPhase
889910 Options = options ,
890911 Deadline = deadline ,
891912 DeadlineSeconds = ( int ) deadline . Subtract ( DateTime . UtcNow ) . TotalSeconds ,
892- AttackerFactions = attackerFactionShortcuts ,
913+ AttackerFactions = snapshot . AttackerFactionShortcuts ,
893914 } ;
894915 }
895916 else // DefendCollect
896917 {
897- var allDefenderShortcuts = new HashSet < string > ( ) ;
898- var options = new List < PwMatchCommand . VoteOption > ( snapshots . Count ) ;
899- foreach ( var s in snapshots )
918+ var options = snapshot . Options . Select ( s =>
900919 {
901920 var playerIsAttacker = playerName != null && s . AttackerNames . Contains ( playerName ) ;
902921 var canDefend = playerFactionId != null && s . DefenderFactionIds != null && s . DefenderFactionIds . Contains ( playerFactionId . Value ) ;
903- options . Add ( new PwMatchCommand . VoteOption
922+ return new PwMatchCommand . VoteOption
904923 {
905924 PlanetID = s . PlanetId ,
906925 PlanetName = s . PlanetName ,
@@ -917,23 +936,17 @@ private PwMatchCommand StampLobbyCommand(List<OptionSnapshot> snapshots, PwPhase
917936 AttackerAvgWhr = s . AttackerAvgWhr ,
918937 DefenderAvgWhr = s . DefenderAvgWhr ,
919938 WinChance = s . WinChance ,
920- } ) ;
921- if ( s . DefenderFactionIds != null )
922- foreach ( var fid in s . DefenderFactionIds )
923- {
924- var sc = GetFactionShortcut ( fid ) ;
925- if ( sc != null ) allDefenderShortcuts . Add ( sc ) ;
926- }
927- }
939+ } ;
940+ } ) . ToList ( ) ;
928941
929942 var deadline = GetEffectiveDefendDeadline ( ) ;
930943 return new PwMatchCommand ( PwMatchCommand . ModeType . Defend )
931944 {
932945 Options = options ,
933946 Deadline = deadline ,
934947 DeadlineSeconds = ( int ) deadline . Subtract ( DateTime . UtcNow ) . TotalSeconds ,
935- AttackerFactions = attackerFactionShortcuts ,
936- DefenderFactions = allDefenderShortcuts . ToList ( ) ,
948+ AttackerFactions = snapshot . AttackerFactionShortcuts ,
949+ DefenderFactions = snapshot . DefenderFactionShortcuts ,
937950 } ;
938951 }
939952 }
@@ -960,11 +973,8 @@ public void AddAttackOption(Planet planet, int attackerFactionId)
960973 if ( planet . OwnerFactionID == attackerFactionId ) return ;
961974 if ( AttackOptions . Any ( x => x . PlanetID == planet . PlanetID && x . AttackerFactionID == attackerFactionId ) ) return ;
962975
963- using ( var db = new ZkDataContext ( ) )
964- {
965- var attackerFaction = db . Factions . Find ( attackerFactionId ) ;
966- if ( attackerFaction == null || ! planet . CanMatchMakerPlay ( attackerFaction ) ) return ;
967- }
976+ var attackerFaction = factions . FirstOrDefault ( f => f . FactionID == attackerFactionId ) ;
977+ if ( attackerFaction == null || ! planet . CanMatchMakerPlay ( attackerFaction ) ) return ;
968978
969979 InternalAddOption ( planet , attackerFactionId ) ;
970980 UpdateLobby ( ) ;
@@ -1244,9 +1254,9 @@ private async Task UpdateLobby()
12441254 }
12451255
12461256 // compute viewer-invariant data once, stamp per-viewer flags in parallel send fan-out
1247- var snapshots = ComputeOptionSnapshots ( Phase ) ;
1257+ var snapshot = ComputeLobbySnapshot ( Phase ) ;
12481258 var phase = Phase ;
1249- await Task . WhenAll ( users . Select ( u => u . SendCommand ( StampLobbyCommand ( snapshots , phase , u . Name , u . User . Faction ) ) ) ) ;
1259+ await Task . WhenAll ( users . Select ( u => u . SendCommand ( StampLobbyCommand ( snapshot , phase , u . Name , u . User . Faction ) ) ) ) ;
12501260 SaveStateToDb ( ) ;
12511261 }
12521262
0 commit comments