View Issue Details

IDProjectCategoryLast Update
0025669AI War 2Crash/ExceptionOct 27, 2021 6:42 am
Reportertom.prince Assigned Totom.prince  
Status resolvedResolutionfixed 
Product VersionBeta 3.712 Loading Hotfix 
Fixed in VersionBeta 3.740 Code Panopticon 
Summary0025669: Multiple Spire Cities don't appear to properly bolster a fleet.
DescriptionIt looks like ''RecalculateSpireCityMobileFleetContents_MainThreadSimOnly'' uses ''GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates'' to add ship cap to the fleet from each city. However, (based on looking at the disassembly) that function only works for ''FleetCategory.PlayerMobile'' and otherwise delegates to ''GetOrAddMembershipGroupBasedOnSquadType_AssumeNoDuplicates''. If that function needs the category check, either ''PlayerCustomCityFedMobile'' should be added, or the bolstering logic needs to be changed to use ''GetOrAddMembershipGroupBasedOnSquadType_AssumeNoDuplicates''.

The other overload of ''GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates'' can't obviously be used, since it takes a ''byte'' instead of an ''int'' for the differentiatior and I assume that is too narrow to pass the ''FleetID'' into, without possibly having a collision.
TagsNo tags attached.

Activities

tom.prince

Oct 17, 2021 5:29 pm

developer   ~0062977

I was also surprised that the frigate-per-city was hard-coded in the code, rather than provided by the spire hub structure, though I guess that may be because they are not part of part of the ''SpireCity'' build category, so aren't iterated over.

tom.prince

Oct 17, 2021 5:41 pm

developer   ~0062978

> I was also surprised that the frigate-per-city was hard-coded in the code, rather than provided by the spire hub structure, though I guess that may be because they are not part of part of the ''SpireCity'' build category, so aren't iterated over.

It also appears that this logic is incorrect, as isn't combined with the value from ''ship_type_name_to_grant_more_of_in_custom_fleet''.

tom.prince

Oct 17, 2021 6:58 pm

developer   ~0062979

I've attached a few patches that address some of the issues here:

1. A patch that fixes up the logic around frigates, by moving it to be provided by the spire hub (and adding a new tag to the spire buildings that provide ships)
2. A workaround for ''GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates'' that uses the alternate overload, but this should really be fixed in core instead, as this patch narrows the `FleetID` to a byte, so will cause problems if two spire cities have the same fleet id modulo 256.
3. A patch that will cleanup spire fleet members that aren't bolstered by a specific city due to this bug. This isn't a great implementation, as it runs every tick, and just destroys the ships. However, as I'm guessing the save format may be broken again, this is likely unnecessary.
spire-frigate-tracking.patch (5,653 bytes)   
# HG changeset patch
# User Tom Prince <[email protected]>
# Date 1634510205 21600
#      Sun Oct 17 16:36:45 2021 -0600
# Node ID 6e080cd656aa4cbfaf6e3f4064e280c4088339d5
# Parent  f28b7cdbcd3024b2a3dc3244babbf8599a770a4a
Make Spire Frigates be properly tracked.

diff --git CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs
--- CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs
+++ CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs
@@ -1941,7 +1941,7 @@ namespace Arcen.AIW2.External
         public void RecalculateSpireCityMobileFleetContents_MainThreadSimOnly( Faction faction, ArcenHostOnlySimContext Context )
         {
             spireShipsToAdd.Clear();
-            List<GameEntityTypeData> spireCityBuildings = GameEntityTypeDataTable.Instance.GetAllRowsWithTagOrNull( "SpireCityBuildMenu" );
+            List<GameEntityTypeData> spireCityBuildings = GameEntityTypeDataTable.Instance.GetAllRowsWithTagOrNull( "SpireFeedsFleet" );
 
             int maxMarkLevelOfAnyCity = 0;
 
@@ -2115,7 +2115,6 @@ namespace Arcen.AIW2.External
 
                 int cruisersToHave = 0;
                 int battleshipsToHave = 0;
-                int frigatesToHave = 1;
 
                 if ( fleetForCity.MaxMarkLevelOfAnyInFleet >= 6 ) //city mark 6 or higher
                 {
@@ -2139,13 +2138,6 @@ namespace Arcen.AIW2.External
                     if ( battleshipMem.ExplicitBaseSquadCap < battleshipsToHave )
                         battleshipMem.ExplicitBaseSquadCap = battleshipsToHave;
                 }
-
-                if ( frigatesToHave > 0 && !isCityItselfDisabled ) //increase the cap only if the city is not disabled
-                {
-                    FleetMembership battleshipMem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( GameEntityTypeDataTable.Instance.GetRowByName( "SpireFrigate" ), fleetForCity.FleetID );
-                    if ( battleshipMem.ExplicitBaseSquadCap < frigatesToHave )
-                        battleshipMem.ExplicitBaseSquadCap = frigatesToHave;
-                }
                 return DelReturn.Continue;
             } );
 
diff --git Expansions/1_The_Spire_Rises/GameData/Configuration/GameEntity/TSR_Ships_Spire.xml Expansions/1_The_Spire_Rises/GameData/Configuration/GameEntity/TSR_Ships_Spire.xml
--- Expansions/1_The_Spire_Rises/GameData/Configuration/GameEntity/TSR_Ships_Spire.xml
+++ Expansions/1_The_Spire_Rises/GameData/Configuration/GameEntity/TSR_Ships_Spire.xml
@@ -543,7 +543,7 @@
   ************************************************************************************************
   ************************************************************************************************--> 
 
-    <entity name="SpireCityHub" tags="SpireCity"
+    <entity name="SpireCityHub" tags="SpireCity,SpireFeedsFleet"
 			special_entity_type="CityCenter" draw_in_galaxy_view="true"
       name_for_city_center="Spire City" name_for_city_sockets="Spire Casing"
 			visuals="Assets/_DLC1/FallenSpire/SpireCityHub/SpireCityHub.prefab" visuals_bundle="exp1_the_spire_rises"
@@ -574,11 +574,12 @@
 			description_appender_dll="AIWarExternalCode"
 			description_appender_type="Arcen.AIW2.External.SpireHubDescriptionAppender"
 			shows_on_galaxy_map_for_explored_planets="true" always_visible_to_players="true"
+			ship_type_name_to_grant_more_of_in_custom_fleet="SpireFrigate" ship_type_count_to_grant_more_of_in_custom_fleet="1"
 			>
       <metal_flow purpose="SelfConstruction" throughput="2500"/>
     </entity>
 
-    <entity name="SpireGalacticCapitol" tags="SpireCity"
+    <entity name="SpireGalacticCapitol" tags="SpireCity,SpireFeedsFleet"
             special_entity_type="CityCenter" draw_in_galaxy_view="true"
 						name_for_city_center="Spire City" name_for_city_sockets="Spire Casing"
             visuals="Assets/_DLC1/FallenSpire/SpireGalacticCapitol/SpireGalacticCapitol.prefab" visuals_bundle="exp1_the_spire_rises"
@@ -608,6 +609,7 @@
 			description_appender_dll="AIWarExternalCode"
 			description_appender_type="Arcen.AIW2.External.SpireHubDescriptionAppender"
 			shows_on_galaxy_map_for_explored_planets="true" always_visible_to_players="true"
+			ship_type_name_to_grant_more_of_in_custom_fleet="SpireFrigate" ship_type_count_to_grant_more_of_in_custom_fleet="1"
 			>
       <metal_flow purpose="SelfConstruction" throughput="2500"/>
     <system name="HeavyBeamCannon" display_name="Spire Coil Beam"
@@ -770,7 +772,7 @@
     </entity>
 
     <entity name="SpireFrigateNeuralNet"
-            tags="SpireCityBuildMenu"
+            tags="SpireCityBuildMenu,SpireFeedsFleet"
             visuals="Assets/_DLC1/FallenSpire/SpireFrigateNeuralNet/SpireFrigateNeuralNet.prefab" visuals_bundle="exp1_the_spire_rises"
             icon_name="Ships1/NeuralNet" icon_overlay="Overlays2/NeuralNet_SpireFrigate" gimbal_icon_size_multiplier="1.5"
             voice_group="Spire"
@@ -817,7 +819,7 @@
 
 
     <entity name="SpireDestroyerNeuralNet"
-            tags="SpireCityBuildMenu"
+            tags="SpireCityBuildMenu,SpireFeedsFleet"
             visuals="Assets/_DLC1/FallenSpire/SpireDestroyerNeuralNet/SpireDestroyerNeuralNet.prefab" visuals_bundle="exp1_the_spire_rises"
             icon_name="Ships1/NeuralNet" icon_overlay="Overlays2/NeuralNet_SpireDestroyer" gimbal_icon_size_multiplier="1.5"
             voice_group="Spire"
spire-frigate-tracking.patch (5,653 bytes)   
workaround-for-withuniqueidforduplicates-filtering.patch (4,520 bytes)   
# HG changeset patch
# User Tom Prince <[email protected]>
# Date 1634510400 21600
#      Sun Oct 17 16:40:00 2021 -0600
# Node ID 78137f5317ded8b7573bc5f417738b0203595836
# Parent  6e080cd656aa4cbfaf6e3f4064e280c4088339d5
Work around ``GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates`` category filtering.

This uses the alternate overload of ``GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates``, by passing in
a default cap. However, this has a bug that we can only use a byte for the differentiator. Hopefully every spire city
will have differnet fleet IDs module 256.

diff --git CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs
--- CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs
+++ CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs
@@ -2011,8 +2011,12 @@ namespace Arcen.AIW2.External
 
                         otherMobileFleet.DoForMemberGroupsUnsorted_Sim( delegate ( FleetMembership mem )
                         {
+                            if ( mem.FedHereFromCityFleetID == 0 ) {
+                                mem.DespawnAllContentsFromNoLongerBolstering( Context );
+                                return DelReturn.RemoveAndContinue; //remove the entire line!
+                            }
                             //whoops, this is fed here from us!  Time to clear it, since we're not bolstering that.
-                            if ( mem.FedHereFromCityFleetID == fleetForCity.FleetID )
+                            if ( mem.FedHereFromCityFleetID == (byte)fleetForCity.FleetID )
                             {
                                 mem.DespawnAllContentsFromNoLongerBolstering( Context );
                                 return DelReturn.RemoveAndContinue; //remove the entire line!
@@ -2102,7 +2106,8 @@ namespace Arcen.AIW2.External
                         continue;
                     }
                     int numberToHave = kv.Value;
-                    FleetMembership mem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( typeData, fleetForCity.FleetID );
+                    // Pass in numberToHave to get version of GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates that doesn't care about FleetCategory
+                    FleetMembership mem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( typeData, (byte)fleetForCity.FleetID, 0);
                     //increase the cap only if the city is not disabled
                     if ( mem.ExplicitBaseSquadCap < numberToHave && !isCityItselfDisabled )
                         mem.ExplicitBaseSquadCap = numberToHave;
@@ -2127,14 +2132,14 @@ namespace Arcen.AIW2.External
 
                 if ( cruisersToHave > 0 && !isCityItselfDisabled ) //increase the cap only if the city is not disabled
                 {
-                    FleetMembership cruiserMem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( GameEntityTypeDataTable.Instance.GetRowByName( "SpireCruiser" ), fleetForCity.FleetID );
+                    FleetMembership cruiserMem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( GameEntityTypeDataTable.Instance.GetRowByName( "SpireCruiser" ), (byte) fleetForCity.FleetID, 0 );
                     if ( cruiserMem.ExplicitBaseSquadCap < cruisersToHave )
                         cruiserMem.ExplicitBaseSquadCap = cruisersToHave;
                 }
 
                 if ( battleshipsToHave > 0 && !isCityItselfDisabled ) //increase the cap only if the city is not disabled
                 {
-                    FleetMembership battleshipMem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( GameEntityTypeDataTable.Instance.GetRowByName( "SpireBattleship" ), fleetForCity.FleetID );
+                    FleetMembership battleshipMem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( GameEntityTypeDataTable.Instance.GetRowByName( "SpireBattleship" ), (byte) fleetForCity.FleetID, 0 );
                     if ( battleshipMem.ExplicitBaseSquadCap < battleshipsToHave )
                         battleshipMem.ExplicitBaseSquadCap = battleshipsToHave;
                 }
cleanup-for-missing-bolster-data.patch (4,520 bytes)   
# HG changeset patch
# User Tom Prince <[email protected]>
# Date 1634510400 21600
#      Sun Oct 17 16:40:00 2021 -0600
# Node ID 78137f5317ded8b7573bc5f417738b0203595836
# Parent  6e080cd656aa4cbfaf6e3f4064e280c4088339d5
Work around ``GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates`` category filtering.

This uses the alternate overload of ``GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates``, by passing in
a default cap. However, this has a bug that we can only use a byte for the differentiator. Hopefully every spire city
will have differnet fleet IDs module 256.

diff --git CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs
--- CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs
+++ CodeExternal/AIWarExternalDeepProcessingCode/src/DeepInfo/DLC1/FallenSpireFactionDeepInfo.cs
@@ -2011,8 +2011,12 @@ namespace Arcen.AIW2.External
 
                         otherMobileFleet.DoForMemberGroupsUnsorted_Sim( delegate ( FleetMembership mem )
                         {
+                            if ( mem.FedHereFromCityFleetID == 0 ) {
+                                mem.DespawnAllContentsFromNoLongerBolstering( Context );
+                                return DelReturn.RemoveAndContinue; //remove the entire line!
+                            }
                             //whoops, this is fed here from us!  Time to clear it, since we're not bolstering that.
-                            if ( mem.FedHereFromCityFleetID == fleetForCity.FleetID )
+                            if ( mem.FedHereFromCityFleetID == (byte)fleetForCity.FleetID )
                             {
                                 mem.DespawnAllContentsFromNoLongerBolstering( Context );
                                 return DelReturn.RemoveAndContinue; //remove the entire line!
@@ -2102,7 +2106,8 @@ namespace Arcen.AIW2.External
                         continue;
                     }
                     int numberToHave = kv.Value;
-                    FleetMembership mem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( typeData, fleetForCity.FleetID );
+                    // Pass in numberToHave to get version of GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates that doesn't care about FleetCategory
+                    FleetMembership mem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( typeData, (byte)fleetForCity.FleetID, 0);
                     //increase the cap only if the city is not disabled
                     if ( mem.ExplicitBaseSquadCap < numberToHave && !isCityItselfDisabled )
                         mem.ExplicitBaseSquadCap = numberToHave;
@@ -2127,14 +2132,14 @@ namespace Arcen.AIW2.External
 
                 if ( cruisersToHave > 0 && !isCityItselfDisabled ) //increase the cap only if the city is not disabled
                 {
-                    FleetMembership cruiserMem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( GameEntityTypeDataTable.Instance.GetRowByName( "SpireCruiser" ), fleetForCity.FleetID );
+                    FleetMembership cruiserMem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( GameEntityTypeDataTable.Instance.GetRowByName( "SpireCruiser" ), (byte) fleetForCity.FleetID, 0 );
                     if ( cruiserMem.ExplicitBaseSquadCap < cruisersToHave )
                         cruiserMem.ExplicitBaseSquadCap = cruisersToHave;
                 }
 
                 if ( battleshipsToHave > 0 && !isCityItselfDisabled ) //increase the cap only if the city is not disabled
                 {
-                    FleetMembership battleshipMem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( GameEntityTypeDataTable.Instance.GetRowByName( "SpireBattleship" ), fleetForCity.FleetID );
+                    FleetMembership battleshipMem = fleetForMobile.GetOrAddMembershipGroupBasedOnSquadType_WithUniqueIDForDuplicates( GameEntityTypeDataTable.Instance.GetRowByName( "SpireBattleship" ), (byte) fleetForCity.FleetID, 0 );
                     if ( battleshipMem.ExplicitBaseSquadCap < battleshipsToHave )
                         battleshipMem.ExplicitBaseSquadCap = battleshipsToHave;
                 }

Issue History

Date Modified Username Field Change
Oct 17, 2021 5:21 pm tom.prince New Issue
Oct 17, 2021 5:29 pm tom.prince Note Added: 0062977
Oct 17, 2021 5:41 pm tom.prince Note Added: 0062978
Oct 17, 2021 6:58 pm tom.prince Note Added: 0062979
Oct 17, 2021 6:58 pm tom.prince File Added: spire-frigate-tracking.patch
Oct 17, 2021 6:58 pm tom.prince File Added: workaround-for-withuniqueidforduplicates-filtering.patch
Oct 17, 2021 6:58 pm tom.prince File Added: cleanup-for-missing-bolster-data.patch
Oct 17, 2021 6:59 pm tom.prince Description Updated
Oct 27, 2021 6:42 am tom.prince Assigned To => tom.prince
Oct 27, 2021 6:42 am tom.prince Status new => resolved
Oct 27, 2021 6:42 am tom.prince Resolution open => fixed
Oct 27, 2021 6:42 am tom.prince Fixed in Version => Beta 3.740 Code Panopticon