Mainly more hauling optimizations, utilizing linked storage groups
This also adds an experimental, usually slower, threaded gas grid mode and implements a workaround for the whole mod and game failing to start when opening prepatcher's mod manager with unfortunate timing
This commit is contained in:
@@ -50,4 +50,43 @@ public sealed class HaulDestinationManagerPatches : ClassWithFishPrepatches
|
||||
__instance.Cache().OnPriorityChanged(__instance);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CompareSlotGroupPrioritiesDescendingPatch : FishPrepatch
|
||||
{
|
||||
public override List<Type> LinkedPatches { get; }
|
||||
= [typeof(StoreUtilityPrepatches.TryFindBestBetterStoreCellForPatch)];
|
||||
|
||||
public override string? Description { get; }
|
||||
= "Modifies storage sort order within the haul destination manager to put groups of linked storages first "
|
||||
+ "within their priority, descending by size and then loadID. "
|
||||
+ "StoreUtilityPrepatches:TryFindBestBetterStoreCellFor requires this";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= methodof(HaulDestinationManager.CompareSlotGroupPrioritiesDescending);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Postfix(int __result, SlotGroup a, SlotGroup b)
|
||||
{
|
||||
if (__result != 0)
|
||||
return __result;
|
||||
|
||||
var storageSettingsGroupA = a.TryGetStorageGroup();
|
||||
var storageSettingsGroupB = b.TryGetStorageGroup();
|
||||
|
||||
return storageSettingsGroupA == null
|
||||
? storageSettingsGroupB == null
|
||||
? 0
|
||||
: 1
|
||||
: storageSettingsGroupB == null
|
||||
? -1
|
||||
: CompareStorageSettingsGroups(storageSettingsGroupA, storageSettingsGroupB);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static int CompareStorageSettingsGroups(StorageGroup a, StorageGroup b)
|
||||
{
|
||||
var result = b.MemberCount.CompareTo(a.MemberCount);
|
||||
return result != 0 ? result : a.loadID.CompareTo(b.loadID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,12 @@ public record struct RimfactoryExtensionCacheValue
|
||||
? slotGroupParent.def.modExtensions?.Find(_extensionPredicate)
|
||||
: null;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool HasExtension(Thing slotGroupParent)
|
||||
=> RimfactoryTypes.ModExtension != null
|
||||
&& (slotGroupParent.def.modExtensions?.Exists(_extensionPredicate)
|
||||
?? false);
|
||||
=> RimfactoryTypes.ModExtension != null && TestExtension(slotGroupParent);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static bool TestExtension(Thing slotGroupParent) => slotGroupParent.TryGetRimfactoryExtension() != null;
|
||||
|
||||
static RimfactoryExtensionCacheValue() => EnsureInitialized();
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using FisheryLib.Pools;
|
||||
using PerformanceFish.Events;
|
||||
|
||||
namespace PerformanceFish.Hauling;
|
||||
|
||||
@@ -17,64 +16,11 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
public sealed class AllowedToAcceptPatch : FirstPriorityFishPatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "StorageSettings and capacity caching. High impact in colonies with multiple full storage buildings.";
|
||||
= "Caches results of storage settings checks for things when hauling";
|
||||
|
||||
public override Expression<Action> TargetMethod { get; }
|
||||
= static () => default(StorageSettings)!.AllowedToAccept(default(Thing));
|
||||
|
||||
protected internal override void OnPatchingCompleted() => ThingEvents.Initialized += TryRegister;
|
||||
|
||||
public static void TryRegister(Thing thing)
|
||||
{
|
||||
var thingEvents = thing.Events();
|
||||
|
||||
if (thing is ISlotGroupParent)
|
||||
thingEvents.Spawned += InitializeSlotGroupParent;
|
||||
|
||||
if (!thing.IsItem())
|
||||
return;
|
||||
|
||||
thingEvents.RegisteredAtThingGrid += TryNotifyReceivedThing;
|
||||
thingEvents.DeregisteredAtThingGrid += TryNotifyLostThing;
|
||||
}
|
||||
|
||||
public static void TryNotifyReceivedThing(Thing thing, Map map, in IntVec3 cell)
|
||||
{
|
||||
if (cell.GetSlotGroupParent(map) is { } parent and Thing)
|
||||
parent.GetStoreSettings()?.Cache().Notify_ReceivedThing(thing);
|
||||
}
|
||||
|
||||
public static void TryNotifyLostThing(Thing thing, Map map, in IntVec3 cell)
|
||||
{
|
||||
if (cell.GetSlotGroupParent(map) is { } parent and Thing)
|
||||
parent.GetStoreSettings()?.Cache().Notify_LostThing(thing);
|
||||
}
|
||||
|
||||
public static void InitializeSlotGroupParent(Thing thing, Map map)
|
||||
{
|
||||
var slotGroupParent = (ISlotGroupParent)thing;
|
||||
|
||||
if (slotGroupParent.GetStoreSettings() is not { } storeSettings)
|
||||
return;
|
||||
|
||||
ref var cache = ref storeSettings.Cache();
|
||||
cache.Initialize(thing);
|
||||
|
||||
var slotCellsList = slotGroupParent.AllSlotCellsList();
|
||||
for (var i = slotCellsList.Count; i-- > 0;)
|
||||
{
|
||||
var thingsAtCell = slotCellsList[i].GetThingListUnchecked(map);
|
||||
for (var j = thingsAtCell.Count; j-- > 0;)
|
||||
{
|
||||
var storedThing = thingsAtCell[j];
|
||||
if (!storedThing.IsItem() || cache.Contains(storedThing))
|
||||
continue;
|
||||
|
||||
cache.Notify_ReceivedThing(storedThing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(StorageSettings __instance, Thing t, ref bool __result, out bool __state)
|
||||
{
|
||||
@@ -83,28 +29,11 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
if (StorageSettingsCache.IsDirty(cachedAllowedToAcceptValue))
|
||||
return __state = true;
|
||||
|
||||
__result = cachedAllowedToAcceptValue.AsBool() && CapacityAllows(__instance, t);
|
||||
__result = cachedAllowedToAcceptValue.AsBool() /*&& CapacityAllows(__instance, t)*/;
|
||||
|
||||
return __state = false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool CapacityAllows(StorageSettings __instance, Thing t)
|
||||
{
|
||||
if (__instance.owner is not Thing ownerThing)
|
||||
return true;
|
||||
|
||||
ref var cache = ref __instance.Cache();
|
||||
Debug.VerifyItemCount(ownerThing, ref cache);
|
||||
|
||||
return cache.FreeSlots > 0
|
||||
|| (t.TryGetMap() is { } map && t.Position.GetSlotGroupParent(map) == ownerThing)
|
||||
|| AcceptsForStacking(ref cache, t);
|
||||
}
|
||||
|
||||
public static bool AcceptsForStacking(ref StorageSettingsCache cache, Thing t)
|
||||
=> cache.StoredThingsOfDef(t.def).ContainsThingStackableWith(t);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Postfix(StorageSettings __instance, Thing t, bool __result, bool __state)
|
||||
{
|
||||
@@ -121,18 +50,61 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
|
||||
public sealed class TryNotifyChanged_Patch : FishPatch
|
||||
{
|
||||
public override List<Type> LinkedPatches { get; } = [typeof(AllowedToAcceptPatch)];
|
||||
|
||||
public static event Action<StorageSettings>? Changed;
|
||||
|
||||
public override string Description { get; } = "StorageSettings cache resetting";
|
||||
|
||||
public override Expression<Action> TargetMethod { get; }
|
||||
= static () => default(StorageSettings)!.TryNotifyChanged();
|
||||
public override MethodBase? TargetMethodInfo { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(StorageSettings), nameof(StorageSettings.TryNotifyChanged));
|
||||
|
||||
public static void Postfix(StorageSettings __instance)
|
||||
{
|
||||
__instance.Cache().OnSettingsChanged(__instance);
|
||||
|
||||
if (__instance.owner is StorageGroup group)
|
||||
NotifyGroupMembers(group);
|
||||
|
||||
Changed?.Invoke(__instance);
|
||||
}
|
||||
|
||||
public static void NotifyGroupMembers(StorageGroup group)
|
||||
{
|
||||
var groupMembers = group.members;
|
||||
for (var i = 0; i < groupMembers.Count; i++)
|
||||
{
|
||||
var thingStoreSettings = groupMembers[i].ThingStoreSettings;
|
||||
thingStoreSettings.Cache().OnSettingsChanged(thingStoreSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void InitializeSlotGroupParent(Thing thing, Map map)
|
||||
{
|
||||
var slotGroupParent = (ISlotGroupParent)thing;
|
||||
|
||||
slotGroupParent.TryGetGroupStoreSettings()?.Cache().Initialize(null);
|
||||
|
||||
if (slotGroupParent.GetThingStoreSettings() is not { } storeSettings)
|
||||
return;
|
||||
|
||||
ref var cache = ref storeSettings.Cache();
|
||||
cache.Initialize(thing);
|
||||
|
||||
var slotCellsList = slotGroupParent.AllSlotCellsList();
|
||||
for (var i = slotCellsList.Count; i-- > 0;)
|
||||
{
|
||||
var thingsAtCell = slotCellsList[i].GetThingListUnchecked(map);
|
||||
for (var j = thingsAtCell.Count; j-- > 0;)
|
||||
{
|
||||
var storedThing = thingsAtCell[j];
|
||||
if (!storedThing.IsItem() || cache.Contains(storedThing))
|
||||
continue;
|
||||
|
||||
cache.Notify_ReceivedThing(storedThing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record struct StorageSettingsCache
|
||||
@@ -153,7 +125,7 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
|
||||
public event Action<StorageSettings>? SettingsChanged;
|
||||
|
||||
public int TotalSlots => _parent!.TrueTotalSlots(_lwmProps, _rimfactoryExtension);
|
||||
public int TotalSlots => _parent?.TrueTotalSlots(_lwmProps, _rimfactoryExtension) ?? 0xFFFFFFF;
|
||||
|
||||
public List<Thing> StoredThingsOfDef(ThingDef def) => _storedThingsByDef.GetOrAdd(def.shortHash);
|
||||
|
||||
@@ -163,13 +135,13 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
public void Update(int thingIDNumber, bool allowedToAccept)
|
||||
=> AllowedToAccept[thingIDNumber] = allowedToAccept.AsInt();
|
||||
|
||||
public void Initialize(Thing slotGroupParent)
|
||||
public void Initialize(Thing? slotGroupParent)
|
||||
{
|
||||
_parent = slotGroupParent;
|
||||
_storedThingsByDef.Clear();
|
||||
AllowedToAccept.Clear();
|
||||
_lwmProps = slotGroupParent.TryGetLwmCompProperties();
|
||||
_rimfactoryExtension = slotGroupParent.TryGetRimfactoryExtension();
|
||||
_lwmProps = slotGroupParent?.TryGetLwmCompProperties();
|
||||
_rimfactoryExtension = slotGroupParent?.TryGetRimfactoryExtension();
|
||||
FreeSlots = TotalSlots;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// #define STORAGE_GROUP_DEBUG
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using PerformanceFish.Events;
|
||||
@@ -89,13 +93,15 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
{
|
||||
public override List<Type> LinkedPatches { get; } =
|
||||
[
|
||||
typeof(SlotGroupPrepatches.Notify_AddedCellPatch), typeof(SlotGroupPrepatches.Notify_LostCellPatch)
|
||||
typeof(SlotGroupPrepatches.Notify_AddedCellPatch), typeof(SlotGroupPrepatches.Notify_LostCellPatch),
|
||||
typeof(StorageSettingsPatches.AllowedToAcceptPatch)
|
||||
];
|
||||
|
||||
public override string? Description { get; }
|
||||
= "Optimizes hauling by splitting storage areas into smaller regions with cached capacity information. "
|
||||
+ "Additionally makes lookups more thorough when having the 'Improve hauling accuracy' setting enabled, "
|
||||
+ "raising the odds of getting closer cells as haul destinations";
|
||||
+ "raising the odds of getting closer cells as haul destinations. StorageSettingsPatches:AllowedToAccept "
|
||||
+ "requires this patch due to shared caches";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= methodof(StoreUtility.TryFindBestBetterStoreCellForWorker);
|
||||
@@ -109,6 +115,7 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
if (thing is ISlotGroupParent)
|
||||
{
|
||||
thingEvents.Spawned += NotifySlotGroupCellCountChanged;
|
||||
thingEvents.Spawned += StorageSettingsPatches.InitializeSlotGroupParent;
|
||||
thingEvents.DeSpawned += NotifySlotGroupCellCountChanged;
|
||||
}
|
||||
|
||||
@@ -126,10 +133,20 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
=> slotGroupParent.GetSlotGroup()?.NotifyCellCountChanged();
|
||||
|
||||
public static void TryNotifyReceivedThing(Thing thing, Map map, in IntVec3 cell)
|
||||
=> map.StorageDistrictGrid()[cell]?.AddThing(thing);
|
||||
{
|
||||
if (cell.GetSlotGroupParent(map) is { } parent and Thing)
|
||||
parent.GetThingStoreSettings()?.Cache().Notify_ReceivedThing(thing);
|
||||
|
||||
map.StorageDistrictGrid()[cell]?.AddThing(thing);
|
||||
}
|
||||
|
||||
public static void TryNotifyLostThing(Thing thing, Map map, in IntVec3 cell)
|
||||
=> map.StorageDistrictGrid()[cell]?.RemoveThing(thing);
|
||||
{
|
||||
if (cell.GetSlotGroupParent(map) is { } parent and Thing)
|
||||
parent.GetThingStoreSettings()?.Cache().Notify_LostThing(thing);
|
||||
|
||||
map.StorageDistrictGrid()[cell]?.RemoveThing(thing);
|
||||
}
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
@@ -138,7 +155,7 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
bool needAccurateResult, ref IntVec3 closestSlot, ref float closestDistSquared,
|
||||
ref StoragePriority foundPriority)
|
||||
{
|
||||
if (slotGroup == null || !slotGroup.parent.Accepts(t))
|
||||
if (slotGroup == null || !slotGroup.parent.Accepts(t) || !CapacityAllows(slotGroup, t))
|
||||
return;
|
||||
|
||||
var thingPosition = t.SpawnedOrAnyParentSpawned ? t.PositionHeld : carrier.PositionHeld;
|
||||
@@ -150,7 +167,7 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
: 0;
|
||||
|
||||
StartOfDistrictLoop:
|
||||
var districts = slotGroup.Districts();
|
||||
var districts = GetDistricts(slotGroup);
|
||||
|
||||
for (var districtIndex = 0; districtIndex < districts.Length; districtIndex++)
|
||||
{
|
||||
@@ -186,6 +203,26 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static StorageDistrict[] GetDistricts(SlotGroup slotGroup) => slotGroup.Districts();
|
||||
// wrapper for analyzer compatibility, as it otherwise throws on ref returns
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool CapacityAllows(SlotGroup slotGroup, Thing t)
|
||||
{
|
||||
var storageSettings = slotGroup.GetThingStoreSettings();
|
||||
if (storageSettings?.owner is not Thing)
|
||||
return true;
|
||||
|
||||
ref var cache = ref storageSettings.Cache();
|
||||
StorageSettingsPatches.Debug.VerifyItemCount((Thing)storageSettings.owner, ref cache);
|
||||
|
||||
return cache.FreeSlots > 0 || AcceptsForStacking(ref cache, t);
|
||||
}
|
||||
|
||||
public static bool AcceptsForStacking(ref StorageSettingsPatches.StorageSettingsCache cache, Thing t)
|
||||
=> cache.StoredThingsOfDef(t.def).ContainsThingStackableWith(t);
|
||||
}
|
||||
|
||||
public sealed class TryFindBestBetterStoreCellForPatch : FishPrepatch
|
||||
@@ -210,6 +247,11 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
foundCell = IntVec3.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if STORAGE_GROUP_DEBUG
|
||||
if (!Debug.LoggedOnce && !(Current.Game?.tickManager?.Paused ?? true))
|
||||
Debug.LogStuff(listInPriorityOrder);
|
||||
#endif
|
||||
|
||||
var foundPriority = currentPriority;
|
||||
var closestDistSquared = (float)int.MaxValue;
|
||||
@@ -232,8 +274,38 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
|
||||
while (groupsOfPriorityCount-- > 0)
|
||||
{
|
||||
StoreUtility.TryFindBestBetterStoreCellForWorker(t, carrier, map, faction, listInPriorityOrder[i++],
|
||||
needAccurateResult, ref closestSlot, ref closestDistSquared, ref foundPriority);
|
||||
var slotGroup = listInPriorityOrder[i++];
|
||||
int otherGroupMembers;
|
||||
var storageGroup = slotGroup.TryGetStorageGroup();
|
||||
if (storageGroup != null)
|
||||
{
|
||||
otherGroupMembers = storageGroup.MemberCount - 1;
|
||||
Debug.VerifyStorageGroup(otherGroupMembers, listInPriorityOrder, i, storageGroup);
|
||||
|
||||
groupsOfPriorityCount -= otherGroupMembers;
|
||||
|
||||
if (!storageGroup.Accepts(t))
|
||||
{
|
||||
i += otherGroupMembers;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
otherGroupMembers = 0;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
StoreUtility.TryFindBestBetterStoreCellForWorker(t, carrier, map, faction, slotGroup,
|
||||
needAccurateResult, ref closestSlot, ref closestDistSquared, ref foundPriority);
|
||||
|
||||
if (otherGroupMembers <= 0)
|
||||
break;
|
||||
|
||||
otherGroupMembers--;
|
||||
slotGroup = listInPriorityOrder[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +332,40 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
// remove for any storage instead of only valid storage to prevent further haul attempts until the next
|
||||
// automatic ListerHaulablesTick cycle
|
||||
}
|
||||
|
||||
public static class Debug
|
||||
{
|
||||
[Conditional("STORAGE_GROUP_DEBUG")]
|
||||
public static void VerifyStorageGroup(int otherGroupMembers, List<SlotGroup> listInPriorityOrder, int i,
|
||||
StorageGroup storageGroup)
|
||||
{
|
||||
for (var b = otherGroupMembers; b-- > 0;)
|
||||
{
|
||||
var otherSlotGroup = listInPriorityOrder[i + b];
|
||||
if (otherSlotGroup.TryGetStorageGroup() == storageGroup)
|
||||
continue;
|
||||
|
||||
Log.Error($"Incorrect haul destination order! Storage '{
|
||||
otherSlotGroup.parent}' was expected to have Storage group '{storageGroup}', but had '{
|
||||
otherSlotGroup.TryGetStorageGroup().ToStringSafe()}' instead");
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("STORAGE_GROUP_DEBUG")]
|
||||
public static void LogStuff(List<SlotGroup> listInPriorityOrder)
|
||||
{
|
||||
LoggedOnce = true;
|
||||
var storageGroups = listInPriorityOrder.Select(static slotGroup => slotGroup.TryGetStorageGroup())
|
||||
.Where(Is.NotNull).Distinct().ToList();
|
||||
Log.Message($"SlotGroup count: {listInPriorityOrder.Count}, StorageGroup count: {
|
||||
storageGroups.Count}, slotGroups in storage groups: {
|
||||
storageGroups.Select(static group => group!.MemberCount).Sum()}, outside of storage groups: {
|
||||
listInPriorityOrder.Select(static slotGroup => slotGroup.TryGetStorageGroup())
|
||||
.Where(Is.Null).Count()}");
|
||||
}
|
||||
|
||||
public static bool LoggedOnce;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class GetSlotGroupPatch : FishPrepatch
|
||||
|
||||
Reference in New Issue
Block a user