View Issue Details

IDProjectCategoryLast Update
0025622AI War 2Bug - GameplayMay 16, 2022 5:16 pm
ReporterZweihand Assigned Totom.prince  
Status resolvedResolutionfixed 
Product VersionBeta 3.704 Unexpected Turbo Sim Speed 
Fixed in VersionBeta 3.740 Code Panopticon 
Summary0025622: going more than 4 hops out does not cause deep strike.
DescriptionDeep strikes currently not occurring when attacking planets more than 4 hops out.
TagsNo tags attached.

Relationships

has duplicate 0025659 resolvedtom.prince Deep strike not triggering 

Activities

Zweihand

Oct 5, 2021 3:01 pm

reporter  

Not Deep Striking.save (211,471 bytes)

tom.prince

Oct 23, 2021 1:42 am

developer   ~0062985

It looks like this is happening because the code that updates the lists of planets AIReservesFactionDeepInfo uses to determine deeps strikes is only called if the reserves are active.

I've attached to patches to fix this. The first just moves the code to the point before we checking if the reserves are active. However, I noticed that the code is iterating over all planets twice in succession, once to set `planet.IsEligibleForDeepStrike` and the one to check that an populate `DeepstrikeEligiblePlanets`. The second patch combines those two loops, so we only need to iterate over all the planets once. (It also isn't clear to me if there is a reason to make the loop single threaded (though maybe the first wants to be parallel and the second serial).
deep-strike-cacl.patch (3,986 bytes)   
# HG changeset patch
# User Tom Prince <[email protected]>
# Date 1634964999 21600
#      Fri Oct 22 22:56:39 2021 -0600
# Node ID f8787f53ae8ccb49c9436aa1940587b6f4179d55
# Parent  264eec5b9289a05934a1c499d832bc6a220803f8
Make sure that deepstrike elligibility is update even when there are no deep strikes.

This was causing deepstrikes to never happen, as no planets would be declared elligible.

diff --git CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/BaseGame/AI/AIReservesFactionDeepInfo.cs CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/BaseGame/AI/AIReservesFactionDeepInfo.cs
--- CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/BaseGame/AI/AIReservesFactionDeepInfo.cs
+++ CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/BaseGame/AI/AIReservesFactionDeepInfo.cs
@@ -301,6 +301,20 @@ namespace Arcen.AIW2.External
                 debugCode = 100;
                 UnassignedShips.Clear();
                 UnassignedShipsByPlanet.Clear();
+
+                //TEACHING_MOMENT: hey, this used to be called from both the Stage3 (on the main sim thread) AND from the background thread.
+                //This is some code (these next two methods) that can be VERY slow, so that's a horrible idea.  But in the past, we had no choice.
+                //Now we can just run this on the LRP thread, and both the simulation thread and the LRP thread can use the results because they are
+                //double-buffered lists.  This can save notable CPU time on a thread that impacts sim speed (the simulation thread), while it still
+                //gets to use the results.
+                //
+                //Quick quiz, though: Who can't use the results?
+                //Answer: the UI, or any of the code in BaseInfo.  So it's getting the nice IsEligibleForDeepStrike serialized variable that it can work from.
+                //The simulation thread, the main/ui thread, and the LRP thread are three different threads, and they can all benefit from the work of each other.
+                //You just have to do that... with a bit of care
+                UpdateDeepstrikeEligiblePlanetsList_CallFromLRPOnly();
+                UpdateDeepstrikePlanetsUnderAttack_CallFromLRPOnly();
+
                 if ( !this.BaseInfo.ReservesActive )
                     return; //we're inactive
                 //if ( !this.BaseInfo.AbsorbShipsMode )
@@ -347,19 +361,6 @@ namespace Arcen.AIW2.External
                 } );
                 this.BaseInfo.DropExistingOrders = false;
 
-                //TEACHING_MOMENT: hey, this used to be called from both the Stage3 (on the main sim thread) AND from the background thread.
-                //This is some code (these next two methods) that can be VERY slow, so that's a horrible idea.  But in the past, we had no choice.
-                //Now we can just run this on the LRP thread, and both the simulation thread and the LRP thread can use the results because they are
-                //double-buffered lists.  This can save notable CPU time on a thread that impacts sim speed (the simulation thread), while it still
-                //gets to use the results.
-                //
-                //Quick quiz, though: Who can't use the results?
-                //Answer: the UI, or any of the code in BaseInfo.  So it's getting the nice IsEligibleForDeepStrike serialized variable that it can work from.
-                //The simulation thread, the main/ui thread, and the LRP thread are three different threads, and they can all benefit from the work of each other.
-                //You just have to do that... with a bit of care
-                UpdateDeepstrikeEligiblePlanetsList_CallFromLRPOnly();
-                UpdateDeepstrikePlanetsUnderAttack_CallFromLRPOnly();
-
                 if ( EligiblePlanetsUnderAttackByPlayer.Count == 0 && !this.BaseInfo.AbsorbShipsMode )
                 {
                     return;
deep-strike-cacl.patch (3,986 bytes)   
deep-strike-combine-loops.patch (11,657 bytes)   
# HG changeset patch
# User Tom Prince <[email protected]>
# Date 1634966751 21600
#      Fri Oct 22 23:25:51 2021 -0600
# Node ID e580586bad15f23079f0c19f2da1d857ca141dcd
# Parent  f8787f53ae8ccb49c9436aa1940587b6f4179d55
Combine the two loops the calculate `planet.IsEligibleForDeepStrike` and `DeepstrikeEligiblePlanets`.

The run one after another and iterate over the same collection.

diff --git CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/BaseGame/AI/AIReservesFactionDeepInfo.cs CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/BaseGame/AI/AIReservesFactionDeepInfo.cs
--- CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/BaseGame/AI/AIReservesFactionDeepInfo.cs
+++ CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/BaseGame/AI/AIReservesFactionDeepInfo.cs
@@ -59,14 +59,46 @@ namespace Arcen.AIW2.External
         #region DoBackgroundThreadExpensiveEligibilityCalculations
         private void DoBackgroundThreadExpensiveEligibilityCalculations( ArcenLongTermIntermittentPlanningContext Context )
         {
+            #region Tracing
+            bool tracing = this.tracing_shortTerm = Engine_AIW2.TraceAtAll && Engine_AIW2.TracingFlags.Has( ArcenTracingFlags.AIReserves );
+            ArcenCharacterBuffer tracingBuffer = this.tracingBuffer_shortTerm;
+            bool debug = false;
+            #endregion
+
+            //TEACHING_MOMENT: Hey, check this out!  Double buffered items can be produced on any thread... but what can background threads safely feed info to?
+            //We _could_ have had the DeepstrikeEligiblePlanets be on BaseInfo, and use it from the UI... except wait a second, that would
+            //be a terrible idea because clients don't call this DeepInfo code.
+            //DeepInfo code can absolutely update lists or variables on BaseInfo, but those need to be serialized or else the client
+            //won't hear anything about it.  In this case, the IsEligibleForDeepStrike variable on planets IS serialized, and is used by the UI.
+            //That's a single bit, which is very efficient to tack on.  Adding a list of DeepstrikeEligiblePlanets on BaseInfo would be redundant info
+            //as well as taking more bandwidth.  So we just calculate it here, use it here, and leave the result for any thread.
+            //
+            //If you ever are wondering why a given list or variable is in either class, that's the reason: it's meant to be saved or seen by clients and the UI, or it's not.
+            //There are plenty of working lists on the BaseInfo classes where they are not saved, but those should ALL be calculated in BaseInfo, not from DeepInfo (or else they're blank for clients).
+            //In the event that something doesn't seem like it belongs on BaseInfo based on those rules, then it should be moved to DeepInfo.  If some UI code then complains, you have a problem.
+            //It's time at that point to either make something serialize, or to decide that something is okay being host-only (like debug data often is).
+            DeepstrikeEligiblePlanets.ClearConstructionListForStartingConstruction();
+
             World_AIW2.Instance.DoForPlanetsSingleThread( false, delegate ( Planet planet )
             {
                 //IsEligibleForDeepStrike will get read by the main thread on the host and in single player.
                 //IsEligibleForDeepStrike will also get communicated to the UI of clients in MP within 2-3 seconds at the absolute most, if not faster
                 planet.IsEligibleForDeepStrike = CalculateIsPlanetEligibleForDeepStrike_VeryExpensiveDoOnBackgroundThreadOnly( planet, Context );
 
+                if ( !planet.IsEligibleForDeepStrike )
+                {
+                    return DelReturn.Continue;
+                }
+                DeepstrikeEligiblePlanets.AddToConstructionList( planet );
+                Faction controllingFaction = planet.GetControllingFaction();
+                if ( tracing && debug && planet.GetPlanetFactionForFaction( controllingFaction ).DataByStance[FactionStance.Hostile].TotalStrength > 0 )
+                    tracingBuffer.Add( planet.Name + " is eligible for the reserves. Total strength not in transports: " + planet.GetPlanetFactionForFaction( controllingFaction ).DataByStance[FactionStance.Hostile].TotalPlayerStrengthNotInTransports + " total strength " + planet.GetPlanetFactionForFaction( controllingFaction ).DataByStance[FactionStance.Hostile].TotalStrength ).Add("\n");
+
+
                 return DelReturn.Continue;
             } );
+
+            DeepstrikeEligiblePlanets.SwitchConstructionToDisplay();
         }
         #endregion
 
@@ -151,47 +183,6 @@ namespace Arcen.AIW2.External
         }
         #endregion
 
-        #region UpdateDeepstrikeEligiblePlanetsList_CallFromLRPOnly
-        private void UpdateDeepstrikeEligiblePlanetsList_CallFromLRPOnly()
-        {
-            #region Tracing
-            bool tracing = this.tracing_shortTerm = Engine_AIW2.TraceAtAll && Engine_AIW2.TracingFlags.Has( ArcenTracingFlags.AIReserves );
-            ArcenCharacterBuffer tracingBuffer = this.tracingBuffer_shortTerm;
-            bool debug = false;
-            #endregion
-
-            //TEACHING_MOMENT: Hey, check this out!  Double buffered items can be produced on any thread... but what can background threads safely feed info to?
-            //We _could_ have had the DeepstrikeEligiblePlanets be on BaseInfo, and use it from the UI... except wait a second, that would
-            //be a terrible idea because clients don't call this DeepInfo code.
-            //DeepInfo code can absolutely update lists or variables on BaseInfo, but those need to be serialized or else the client
-            //won't hear anything about it.  In this case, the IsEligibleForDeepStrike variable on planets IS serialized, and is used by the UI.
-            //That's a single bit, which is very efficient to tack on.  Adding a list of DeepstrikeEligiblePlanets on BaseInfo would be redundant info
-            //as well as taking more bandwidth.  So we just calculate it here, use it here, and leave the result for any thread.
-            //
-            //If you ever are wondering why a given list or variable is in either class, that's the reason: it's meant to be saved or seen by clients and the UI, or it's not.
-            //There are plenty of working lists on the BaseInfo classes where they are not saved, but those should ALL be calculated in BaseInfo, not from DeepInfo (or else they're blank for clients).
-            //In the event that something doesn't seem like it belongs on BaseInfo based on those rules, then it should be moved to DeepInfo.  If some UI code then complains, you have a problem.
-            //It's time at that point to either make something serialize, or to decide that something is okay being host-only (like debug data often is).
-            DeepstrikeEligiblePlanets.ClearConstructionListForStartingConstruction();
-
-            World_AIW2.Instance.DoForPlanetsSingleThread( false, delegate ( Planet planet )
-            {
-                if ( !planet.IsEligibleForDeepStrike )
-                {
-                    return DelReturn.Continue;
-                }
-                DeepstrikeEligiblePlanets.AddToConstructionList( planet );
-                Faction controllingFaction = planet.GetControllingFaction();
-                if ( tracing && debug && planet.GetPlanetFactionForFaction( controllingFaction ).DataByStance[FactionStance.Hostile].TotalStrength > 0 )
-                    tracingBuffer.Add( planet.Name + " is eligible for the reserves. Total strength not in transports: " + planet.GetPlanetFactionForFaction( controllingFaction ).DataByStance[FactionStance.Hostile].TotalPlayerStrengthNotInTransports + " total strength " + planet.GetPlanetFactionForFaction( controllingFaction ).DataByStance[FactionStance.Hostile].TotalStrength ).Add("\n");
-
-                return DelReturn.Continue;
-            } );
-
-            DeepstrikeEligiblePlanets.SwitchConstructionToDisplay();
-        }
-        #endregion
-
         #region UpdateDeepstrikePlanetsUnderAttack_CallFromLRPOnly
         private void UpdateDeepstrikePlanetsUnderAttack_CallFromLRPOnly()
         {
@@ -290,7 +281,18 @@ namespace Arcen.AIW2.External
         //private bool CheckForOutOfDateOrders = false;
         public override void DoLongRangePlanning_OnBackgroundNonSimThread_Subclass( ArcenLongTermIntermittentPlanningContext Context )
         {
+            //TEACHING_MOMENT: hey, this used to be called from both the Stage3 (on the main sim thread) AND from the background thread.
+            //This is some code (these next two methods) that can be VERY slow, so that's a horrible idea.  But in the past, we had no choice.
+            //Now we can just run this on the LRP thread, and both the simulation thread and the LRP thread can use the results because they are
+            //double-buffered lists.  This can save notable CPU time on a thread that impacts sim speed (the simulation thread), while it still
+            //gets to use the results.
+            //
+            //Quick quiz, though: Who can't use the results?
+            //Answer: the UI, or any of the code in BaseInfo.  So it's getting the nice IsEligibleForDeepStrike serialized variable that it can work from.
+            //The simulation thread, the main/ui thread, and the LRP thread are three different threads, and they can all benefit from the work of each other.
+            //You just have to do that... with a bit of care
             this.DoBackgroundThreadExpensiveEligibilityCalculations( Context );
+            UpdateDeepstrikePlanetsUnderAttack_CallFromLRPOnly();
 
             bool tracing = this.tracing_longTerm = Engine_AIW2.TraceAtAll && Engine_AIW2.TracingFlags.Has( ArcenTracingFlags.CPA );
             ArcenCharacterBuffer tracingBuffer = this.tracingBuffer_longTerm;
@@ -302,19 +304,6 @@ namespace Arcen.AIW2.External
                 UnassignedShips.Clear();
                 UnassignedShipsByPlanet.Clear();
 
-                //TEACHING_MOMENT: hey, this used to be called from both the Stage3 (on the main sim thread) AND from the background thread.
-                //This is some code (these next two methods) that can be VERY slow, so that's a horrible idea.  But in the past, we had no choice.
-                //Now we can just run this on the LRP thread, and both the simulation thread and the LRP thread can use the results because they are
-                //double-buffered lists.  This can save notable CPU time on a thread that impacts sim speed (the simulation thread), while it still
-                //gets to use the results.
-                //
-                //Quick quiz, though: Who can't use the results?
-                //Answer: the UI, or any of the code in BaseInfo.  So it's getting the nice IsEligibleForDeepStrike serialized variable that it can work from.
-                //The simulation thread, the main/ui thread, and the LRP thread are three different threads, and they can all benefit from the work of each other.
-                //You just have to do that... with a bit of care
-                UpdateDeepstrikeEligiblePlanetsList_CallFromLRPOnly();
-                UpdateDeepstrikePlanetsUnderAttack_CallFromLRPOnly();
-
                 if ( !this.BaseInfo.ReservesActive )
                     return; //we're inactive
                 //if ( !this.BaseInfo.AbsorbShipsMode )
deep-strike-combine-loops.patch (11,657 bytes)   

tom.prince

Oct 25, 2021 10:50 pm

developer   ~0062999

I think this will be fixed in the latest release, though I can't test the save.

Issue History

Date Modified Username Field Change
Oct 5, 2021 3:01 pm Zweihand New Issue
Oct 5, 2021 3:01 pm Zweihand File Added: Not Deep Striking.save
Oct 16, 2021 6:06 am Daniexpert Relationship added has duplicate 0025659
Oct 16, 2021 6:06 am Daniexpert Relationship replaced related to 0025659
Oct 23, 2021 1:42 am tom.prince Note Added: 0062985
Oct 23, 2021 1:42 am tom.prince File Added: deep-strike-cacl.patch
Oct 23, 2021 1:42 am tom.prince File Added: deep-strike-combine-loops.patch
Oct 25, 2021 10:50 pm tom.prince Assigned To => tom.prince
Oct 25, 2021 10:50 pm tom.prince Status new => resolved
Oct 25, 2021 10:50 pm tom.prince Resolution open => fixed
Oct 25, 2021 10:50 pm tom.prince Fixed in Version => Beta 3.740 Code Panopticon
Oct 25, 2021 10:50 pm tom.prince Note Added: 0062999
Oct 25, 2021 10:51 pm tom.prince Relationship replaced has duplicate 0025659