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:
Binary file not shown.
Binary file not shown.
@@ -3,8 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
// crashes to desktop somehow
|
using Mono.Cecil;
|
||||||
/*using Mono.Cecil;
|
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using PerformanceFish.Prepatching;
|
using PerformanceFish.Prepatching;
|
||||||
|
|
||||||
@@ -14,37 +13,46 @@ public sealed class DefDatabasePatches : ClassWithFishPrepatches
|
|||||||
{
|
{
|
||||||
public sealed class GetNamedPatch : FishPrepatch
|
public sealed class GetNamedPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
|
public override string? Description { get; }
|
||||||
|
= "Optimizes the method with fewer instructions and faster dictionary lookup function";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; } = methodof(DefDatabase<Def>.GetNamed);
|
public override MethodBase TargetMethodBase { get; } = methodof(DefDatabase<Def>.GetNamed);
|
||||||
|
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(GetNamed<Def>);
|
=> ilProcessor.ReplaceBodyWith(Replacement<Def>.GetNamed);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T? GetNamed<T>(string defName, bool errorOnFail = true) where T : Def
|
|
||||||
{
|
|
||||||
var value = DefDatabase<T>.defsByName.TryGetValue(defName);
|
|
||||||
|
|
||||||
if (errorOnFail && value is null)
|
|
||||||
LogMissingDef<T>(defName);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
public static void LogMissingDef<T>(string defName) where T : Def
|
|
||||||
=> Log.Error($"Failed to find {typeof(T)} named {defName}. There are {
|
|
||||||
DefDatabase<T>.defsList.Count} defs of this type loaded.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class GetByShortHashPatch : FishPrepatch
|
public sealed class GetByShortHashPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
|
public override string? Description { get; }
|
||||||
|
= "Optimizes the method with fewer instructions and faster dictionary lookup function";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; } = methodof(DefDatabase<Def>.GetByShortHash);
|
public override MethodBase TargetMethodBase { get; } = methodof(DefDatabase<Def>.GetByShortHash);
|
||||||
|
|
||||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(GetByShortHash<Def>);
|
=> ilProcessor.ReplaceBodyWith(Replacement<Def>.GetByShortHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Replacement<T> where T : Def
|
||||||
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static T? GetByShortHash<T>(ushort shortHash) where T : Def
|
public static T? GetNamed(string defName, bool errorOnFail = true)
|
||||||
|
{
|
||||||
|
var value = DefDatabase<T>.defsByName.TryGetValue(defName);
|
||||||
|
|
||||||
|
if (errorOnFail && value is null)
|
||||||
|
LogMissingDef(defName);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static void LogMissingDef(string defName)
|
||||||
|
=> Log.Error($"Failed to find {typeof(T)} named '{defName}'. DefDatabase<{
|
||||||
|
typeof(T)}> contains a total of {DefDatabase<T>.defsList.Count} loaded defs.");
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T? GetByShortHash(ushort shortHash)
|
||||||
=> DefDatabase<T>.defsByShortHash.TryGetValue(shortHash);
|
=> DefDatabase<T>.defsByShortHash.TryGetValue(shortHash);
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
@@ -26,7 +26,7 @@ public sealed class FishSettings : ModSettings
|
|||||||
if (!TryScribeVersion())
|
if (!TryScribeVersion())
|
||||||
goto DoneLoading;
|
goto DoneLoading;
|
||||||
|
|
||||||
Scribe_Values.Look(ref _threadingEnabled, "ThreadingEnabled");
|
Scribe_Values.Look(ref ThreadingEnabled, "ThreadingEnabled");
|
||||||
Scribe_Values.Look(ref MothballEverything, "MothballEverything");
|
Scribe_Values.Look(ref MothballEverything, "MothballEverything");
|
||||||
Scribe_Values.Look(ref ImproveHaulingAccuracy, "ImproveHaulingAccuracy", true);
|
Scribe_Values.Look(ref ImproveHaulingAccuracy, "ImproveHaulingAccuracy", true);
|
||||||
|
|
||||||
@@ -204,6 +204,10 @@ public sealed class FishSettings : ModSettings
|
|||||||
ls.CheckboxLabeled("Improve hauling accuracy", ref ImproveHaulingAccuracy, ImproveHaulingAccuracyDescription);
|
ls.CheckboxLabeled("Improve hauling accuracy", ref ImproveHaulingAccuracy, ImproveHaulingAccuracyDescription);
|
||||||
ls.Gap();
|
ls.Gap();
|
||||||
ls.CheckboxLabeled("Mothball everything", ref MothballEverything, MothballEverythingDescription);
|
ls.CheckboxLabeled("Mothball everything", ref MothballEverything, MothballEverythingDescription);
|
||||||
|
ls.Gap();
|
||||||
|
ls.CheckboxLabeled("Allow threading", ref ThreadingEnabled, "Currently only affects the gas grid. "
|
||||||
|
+ "Experimental and disabled by default. Requires a high gas coverage on the map to outperform the single "
|
||||||
|
+ "threaded variant. Changes to this setting require a restart.");
|
||||||
|
|
||||||
ls.Gap();
|
ls.Gap();
|
||||||
|
|
||||||
@@ -317,23 +321,9 @@ public sealed class FishSettings : ModSettings
|
|||||||
|
|
||||||
public static bool
|
public static bool
|
||||||
MothballEverything,
|
MothballEverything,
|
||||||
ImproveHaulingAccuracy = true;
|
ImproveHaulingAccuracy = true,
|
||||||
|
ThreadingEnabled;
|
||||||
public static bool ThreadingEnabled
|
|
||||||
{
|
|
||||||
get => false; //_threadingEnabled;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == _threadingEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ThreadingPatches.ForEach(p => p.Enabled = value);
|
|
||||||
_threadingEnabled = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool _threadingEnabled;
|
|
||||||
public static List<FishPatch> ThreadingPatches { get; } = [];
|
|
||||||
private static Rect _scrollRect = new(0f, 0f, 500f, 9001f);
|
private static Rect _scrollRect = new(0f, 0f, 500f, 9001f);
|
||||||
private static Vector2 _scrollPosition;
|
private static Vector2 _scrollPosition;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
// #define GAS_DEBUG_L3 // individual cell changes
|
// #define GAS_DEBUG_L3 // individual cell changes
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
@@ -18,9 +19,6 @@ namespace PerformanceFish;
|
|||||||
|
|
||||||
public sealed class GasGridOptimization : ClassWithFishPrepatches
|
public sealed class GasGridOptimization : ClassWithFishPrepatches
|
||||||
{
|
{
|
||||||
// public static readonly List<ParallelGasGrid> ParallelGasGrids = new();
|
|
||||||
// public static GasGrid[] RegisteredGasGrids = Array.Empty<GasGrid>();
|
|
||||||
|
|
||||||
public sealed class SetDirectPatch : FishPrepatch
|
public sealed class SetDirectPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override bool ShowSettings => false;
|
public override bool ShowSettings => false;
|
||||||
@@ -56,8 +54,6 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
typeof(Notify_ThingSpawnedPatch), typeof(ExposeDataPatch), typeof(MouseOverReadOutOnGUI),
|
typeof(Notify_ThingSpawnedPatch), typeof(ExposeDataPatch), typeof(MouseOverReadOutOnGUI),
|
||||||
typeof(DebugToolsGeneralPushGas)
|
typeof(DebugToolsGeneralPushGas)
|
||||||
];
|
];
|
||||||
|
|
||||||
// public static EventWaitHandle? WaitHandle { get; private set; }
|
|
||||||
|
|
||||||
public static object? MonitorObject { get; private set; }
|
public static object? MonitorObject { get; private set; }
|
||||||
|
|
||||||
@@ -65,8 +61,8 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
= "Optimizes the gas grid to utilize highly performant bit manipulation for much faster processing of "
|
= "Optimizes the gas grid to utilize highly performant bit manipulation for much faster processing of "
|
||||||
+ "cells, usually in buckets of 64 simultaneously checked cells, and threading to process the different "
|
+ "cells, usually in buckets of 64 simultaneously checked cells, and threading to process the different "
|
||||||
+ "gas types in parallel. Additionally exposes gas to xml through the PerformanceFish.GasDef type. "
|
+ "gas types in parallel. Additionally exposes gas to xml through the PerformanceFish.GasDef type. "
|
||||||
+ "Performance impact scales with map size, but is generally large. Currently only utilizing single core "
|
+ "Performance impact scales with map size, but is generally large. Threading requires the threading "
|
||||||
+ "parallelism, threading is planned";
|
+ "enabled setting to be turned on. Default is off";
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= AccessTools.DeclaredMethod(typeof(GasGrid), nameof(GasGrid.Tick));
|
= AccessTools.DeclaredMethod(typeof(GasGrid), nameof(GasGrid.Tick));
|
||||||
@@ -76,102 +72,57 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
public static void ReplacementBody(GasGrid __instance)
|
public static void ReplacementBody(GasGrid __instance)
|
||||||
{
|
{
|
||||||
// Parallel.For(0, ParallelGasGrids.Length, static i => ParallelGasGrids[i].Tick());
|
|
||||||
// (WaitHandle ?? Setup()).Set();
|
|
||||||
|
|
||||||
// if (!RegisteredGasGrids.Contains(__instance))
|
|
||||||
// Setup(__instance);
|
|
||||||
|
|
||||||
// ParallelNoAlloc.PulseRegisteredBackgroundWorkers(__instance.MonitorObject());
|
|
||||||
|
|
||||||
var gasGrids = __instance.ParallelGasGrids();
|
var gasGrids = __instance.ParallelGasGrids();
|
||||||
|
|
||||||
for (var i = 1; i < gasGrids.Length; i++)
|
|
||||||
gasGrids[i].Tick();
|
|
||||||
|
|
||||||
if (gasGrids.Length < 1)
|
if (gasGrids.Length < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (FishSettings.ThreadingEnabled
|
||||||
|
&& TryGetRegisteredGasGridWorkers(__instance, out var monitorObject))
|
||||||
|
{
|
||||||
|
TryPulseRegisteredGasGridWorkers(gasGrids, monitorObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (var i = 1; i < gasGrids.Length; i++)
|
||||||
|
gasGrids[i].Tick();
|
||||||
|
}
|
||||||
|
|
||||||
var firstGasGrid = gasGrids[0];
|
var firstGasGrid = gasGrids[0];
|
||||||
firstGasGrid.Tick();
|
firstGasGrid.Tick();
|
||||||
|
|
||||||
__instance.cycleIndexDiffusion = firstGasGrid.CycleIndexDiffusion;
|
__instance.cycleIndexDiffusion = firstGasGrid.CycleIndexDiffusion;
|
||||||
__instance.cycleIndexDissipation = firstGasGrid.CycleIndexDissipation;
|
__instance.cycleIndexDissipation = firstGasGrid.CycleIndexDissipation;
|
||||||
|
|
||||||
|
if (FishSettings.ThreadingEnabled)
|
||||||
|
WaitForGasGridWorkers(gasGrids);
|
||||||
}
|
}
|
||||||
|
|
||||||
// [MemberNotNull(nameof(WaitHandle))]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
// public static EventWaitHandle Setup()
|
public static bool TryGetRegisteredGasGridWorkers(GasGrid __instance,
|
||||||
// {
|
[NotNullWhen(true)] out object? monitorObject)
|
||||||
// WaitHandle = new ManualResetEvent(false);
|
=> (monitorObject = __instance.map.GasGridMonitorObject()) != null
|
||||||
// for (var i = ParallelGasGrids.Length; i-- > 1;)
|
&& ParallelNoAlloc.HasAnyRegisteredWorkers(monitorObject);
|
||||||
// {
|
|
||||||
// var gasGrid = ParallelGasGrids[i];
|
|
||||||
// ThreadPool.RegisterWaitForSingleObject(WaitHandle, (_, _) => gasGrid.Tick(), null, -1, false);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return WaitHandle;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public static ParallelGasGrid[] Setup(GasGrid instance)
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
// {
|
public static void TryPulseRegisteredGasGridWorkers(ParallelGasGrid[] gasGrids, object monitorObject)
|
||||||
// // lock (ParallelGasGrids)
|
|
||||||
// // {
|
|
||||||
// // if (RegisteredGasGrids.Contains(instance))
|
|
||||||
// // {
|
|
||||||
// // // only possible if 2 threads try registering the same gas grid instance. Rimthreaded? Maybe throw?
|
|
||||||
// // SpinWait.SpinUntil(static () => MonitorObject != null);
|
|
||||||
// // #pragma warning disable CS8774
|
|
||||||
// // return;
|
|
||||||
// // #pragma warning restore CS8774
|
|
||||||
// // }
|
|
||||||
// //
|
|
||||||
// // RegisteredGasGrids = RegisteredGasGrids.Add(instance);
|
|
||||||
// // }
|
|
||||||
//
|
|
||||||
// return new ParallelGasGrid[DefDatabase<GasDef>.DefCount];
|
|
||||||
//
|
|
||||||
// var gasDefsList = DefDatabase<GasDef>.AllDefsListForReading;
|
|
||||||
//
|
|
||||||
// var newGasGrids = new ParallelGasGrid[gasDefsList.Count];
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// for (var i = 0; i < newGasGrids.Length; i++)
|
|
||||||
// newGasGrids[i] = new(instance.map, gasDefsList[i]);
|
|
||||||
//
|
|
||||||
// // ParallelGasGrids.AddRangeFast(newGasGrids);
|
|
||||||
//
|
|
||||||
// instance.MonitorObject() = ParallelNoAlloc.RegisterBackgroundWaitingWorkers(MakeTickActions(newGasGrids,
|
|
||||||
// Math.Max(newGasGrids.Length - 1, 0), 1)); // 1st grid ticked on main thread
|
|
||||||
// }
|
|
||||||
// catch (Exception e)
|
|
||||||
// {
|
|
||||||
// Log.Error($"Caught exception in map with ID {instance.map.uniqueID} and hashCode {instance.map.GetHashCode()}: {e}");
|
|
||||||
// return Array.Empty<ParallelGasGrid>();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return newGasGrids;
|
|
||||||
// }
|
|
||||||
|
|
||||||
private static Action[] MakeTickActions<T>(T gasGrids, int count, int startIndex)
|
|
||||||
where T : IList<ParallelGasGrid>
|
|
||||||
{
|
{
|
||||||
var actions = new Action[count];
|
if (gasGrids.Length > 1)
|
||||||
count += startIndex;
|
ParallelNoAlloc.PulseRegisteredBackgroundWorkers(monitorObject);
|
||||||
for (var i = startIndex; i < count; i++)
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public static void WaitForGasGridWorkers(ParallelGasGrid[] gasGrids)
|
||||||
|
{
|
||||||
|
if (gasGrids.Length <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var currentTick = TickHelper.TicksGame;
|
||||||
|
for (var i = 1; i < gasGrids.Length;)
|
||||||
{
|
{
|
||||||
var gasGrid = gasGrids[i];
|
Thread.MemoryBarrier();
|
||||||
actions[i - startIndex] = () => gasGrid.Tick();
|
if (gasGrids[i].LastFinishedTick == currentTick)
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for save-game compatibility. The two cycle indices get scribed with ExposeData
|
|
||||||
public static void AdvanceCycleIndex(ref int cycleIndex, int area, int increment)
|
|
||||||
{
|
|
||||||
cycleIndex += increment;
|
|
||||||
if (cycleIndex >= area)
|
|
||||||
cycleIndex = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,10 +442,13 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
{
|
{
|
||||||
private ulong[] _gasCoverageForIndicesInRandomOrder;
|
private ulong[] _gasCoverageForIndicesInRandomOrder;
|
||||||
private byte[] _gasDensity;
|
private byte[] _gasDensity;
|
||||||
|
private volatile int _lastFinishedTick = -1;
|
||||||
|
|
||||||
public event Action<ParallelGasGrid>? Ticked;
|
public event Action<ParallelGasGrid>? Ticked;
|
||||||
|
|
||||||
public GasDef GasDef { get; private set; }
|
public GasDef GasDef { get; private set; }
|
||||||
|
|
||||||
|
public int LastFinishedTick => _lastFinishedTick;
|
||||||
|
|
||||||
public int DissipationRate => GasDef.dissipationRate;
|
public int DissipationRate => GasDef.dissipationRate;
|
||||||
|
|
||||||
@@ -783,8 +737,10 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
public void Tick()
|
public void Tick()
|
||||||
{
|
{
|
||||||
if (!CalculateGasEffects)
|
try
|
||||||
{
|
{
|
||||||
|
if (!CalculateGasEffects)
|
||||||
|
{
|
||||||
#if GAS_DEBUG_L1
|
#if GAS_DEBUG_L1
|
||||||
if (CheckCurrentlyContainsGas())
|
if (CheckCurrentlyContainsGas())
|
||||||
{
|
{
|
||||||
@@ -792,34 +748,38 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
|
|||||||
Debug.LogIncorrectAnyGasEverAdded(this);
|
Debug.LogIncorrectAnyGasEverAdded(this);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ticked?.Invoke(this);
|
|
||||||
|
|
||||||
var area = Map.Area;
|
Ticked?.Invoke(this);
|
||||||
// CellsInRandomOrder = Map.cellsInRandomOrder.GetAll();
|
|
||||||
// UpdateCellIndicesInRandomOrder();
|
var area = Map.Area;
|
||||||
// Assigned in PostInitialize instead. Ludeon was likely doing this due to component construction order in
|
// CellsInRandomOrder = Map.cellsInRandomOrder.GetAll();
|
||||||
// Map.ConstructComponents
|
// UpdateCellIndicesInRandomOrder();
|
||||||
|
// Assigned in PostInitialize instead. Ludeon was likely doing this due to component construction order in
|
||||||
|
// Map.ConstructComponents
|
||||||
|
|
||||||
#if GAS_DEBUG_L2
|
#if GAS_DEBUG_L2
|
||||||
using var previousDensity = new PooledArray<byte>(_gasDensity.Length);
|
using var previousDensity = new PooledArray<byte>(_gasDensity.Length);
|
||||||
Array.Copy(_gasDensity, 0, previousDensity.BackingArray, 0, _gasDensity.Length);
|
Array.Copy(_gasDensity, 0, previousDensity.BackingArray, 0, _gasDensity.Length);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_dissipationTicker.TickCells(area, (area + 63) >> 6); // Mathf.CeilToInt(area * (1f / 64f))
|
_dissipationTicker.TickCells(area, (area + 63) >> 6); // Mathf.CeilToInt(area * (1f / 64f))
|
||||||
|
|
||||||
if (Diffuses)
|
if (Diffuses)
|
||||||
_diffusionTicker.TickCells(area, (area + 31) >> 5); // Mathf.CeilToInt(area * (1f / 32f))
|
_diffusionTicker.TickCells(area, (area + 31) >> 5); // Mathf.CeilToInt(area * (1f / 32f))
|
||||||
|
|
||||||
#if GAS_DEBUG_L2
|
#if GAS_DEBUG_L2
|
||||||
Debug.LogTotalGasChange(this, previousDensity);
|
Debug.LogTotalGasChange(this, previousDensity);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Map.IsHashIntervalTick(600))
|
if (Map.IsHashIntervalTick(600))
|
||||||
RecalculateEverHadGas();
|
RecalculateEverHadGas();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lastFinishedTick = TickHelper.TicksGame;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
|||||||
@@ -50,4 +50,43 @@ public sealed class HaulDestinationManagerPatches : ClassWithFishPrepatches
|
|||||||
__instance.Cache().OnPriorityChanged(__instance);
|
__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)
|
? slotGroupParent.def.modExtensions?.Find(_extensionPredicate)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool HasExtension(Thing slotGroupParent)
|
public static bool HasExtension(Thing slotGroupParent)
|
||||||
=> RimfactoryTypes.ModExtension != null
|
=> RimfactoryTypes.ModExtension != null && TestExtension(slotGroupParent);
|
||||||
&& (slotGroupParent.def.modExtensions?.Exists(_extensionPredicate)
|
|
||||||
?? false);
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static bool TestExtension(Thing slotGroupParent) => slotGroupParent.TryGetRimfactoryExtension() != null;
|
||||||
|
|
||||||
static RimfactoryExtensionCacheValue() => EnsureInitialized();
|
static RimfactoryExtensionCacheValue() => EnsureInitialized();
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FisheryLib.Pools;
|
using FisheryLib.Pools;
|
||||||
using PerformanceFish.Events;
|
|
||||||
|
|
||||||
namespace PerformanceFish.Hauling;
|
namespace PerformanceFish.Hauling;
|
||||||
|
|
||||||
@@ -17,64 +16,11 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
|||||||
public sealed class AllowedToAcceptPatch : FirstPriorityFishPatch
|
public sealed class AllowedToAcceptPatch : FirstPriorityFishPatch
|
||||||
{
|
{
|
||||||
public override string Description { get; }
|
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; }
|
public override Expression<Action> TargetMethod { get; }
|
||||||
= static () => default(StorageSettings)!.AllowedToAccept(default(Thing));
|
= 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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool Prefix(StorageSettings __instance, Thing t, ref bool __result, out bool __state)
|
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))
|
if (StorageSettingsCache.IsDirty(cachedAllowedToAcceptValue))
|
||||||
return __state = true;
|
return __state = true;
|
||||||
|
|
||||||
__result = cachedAllowedToAcceptValue.AsBool() && CapacityAllows(__instance, t);
|
__result = cachedAllowedToAcceptValue.AsBool() /*&& CapacityAllows(__instance, t)*/;
|
||||||
|
|
||||||
return __state = false;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Postfix(StorageSettings __instance, Thing t, bool __result, bool __state)
|
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 sealed class TryNotifyChanged_Patch : FishPatch
|
||||||
{
|
{
|
||||||
|
public override List<Type> LinkedPatches { get; } = [typeof(AllowedToAcceptPatch)];
|
||||||
|
|
||||||
public static event Action<StorageSettings>? Changed;
|
public static event Action<StorageSettings>? Changed;
|
||||||
|
|
||||||
public override string Description { get; } = "StorageSettings cache resetting";
|
public override string Description { get; } = "StorageSettings cache resetting";
|
||||||
|
|
||||||
public override Expression<Action> TargetMethod { get; }
|
public override MethodBase? TargetMethodInfo { get; }
|
||||||
= static () => default(StorageSettings)!.TryNotifyChanged();
|
= AccessTools.DeclaredMethod(typeof(StorageSettings), nameof(StorageSettings.TryNotifyChanged));
|
||||||
|
|
||||||
public static void Postfix(StorageSettings __instance)
|
public static void Postfix(StorageSettings __instance)
|
||||||
{
|
{
|
||||||
__instance.Cache().OnSettingsChanged(__instance);
|
__instance.Cache().OnSettingsChanged(__instance);
|
||||||
|
|
||||||
|
if (__instance.owner is StorageGroup group)
|
||||||
|
NotifyGroupMembers(group);
|
||||||
|
|
||||||
Changed?.Invoke(__instance);
|
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
|
public record struct StorageSettingsCache
|
||||||
@@ -153,7 +125,7 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
|||||||
|
|
||||||
public event Action<StorageSettings>? SettingsChanged;
|
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);
|
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)
|
public void Update(int thingIDNumber, bool allowedToAccept)
|
||||||
=> AllowedToAccept[thingIDNumber] = allowedToAccept.AsInt();
|
=> AllowedToAccept[thingIDNumber] = allowedToAccept.AsInt();
|
||||||
|
|
||||||
public void Initialize(Thing slotGroupParent)
|
public void Initialize(Thing? slotGroupParent)
|
||||||
{
|
{
|
||||||
_parent = slotGroupParent;
|
_parent = slotGroupParent;
|
||||||
_storedThingsByDef.Clear();
|
_storedThingsByDef.Clear();
|
||||||
AllowedToAccept.Clear();
|
AllowedToAccept.Clear();
|
||||||
_lwmProps = slotGroupParent.TryGetLwmCompProperties();
|
_lwmProps = slotGroupParent?.TryGetLwmCompProperties();
|
||||||
_rimfactoryExtension = slotGroupParent.TryGetRimfactoryExtension();
|
_rimfactoryExtension = slotGroupParent?.TryGetRimfactoryExtension();
|
||||||
FreeSlots = TotalSlots;
|
FreeSlots = TotalSlots;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// 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;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using PerformanceFish.Events;
|
using PerformanceFish.Events;
|
||||||
@@ -89,13 +93,15 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
|||||||
{
|
{
|
||||||
public override List<Type> LinkedPatches { get; } =
|
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; }
|
public override string? Description { get; }
|
||||||
= "Optimizes hauling by splitting storage areas into smaller regions with cached capacity information. "
|
= "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, "
|
+ "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; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= methodof(StoreUtility.TryFindBestBetterStoreCellForWorker);
|
= methodof(StoreUtility.TryFindBestBetterStoreCellForWorker);
|
||||||
@@ -109,6 +115,7 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
|||||||
if (thing is ISlotGroupParent)
|
if (thing is ISlotGroupParent)
|
||||||
{
|
{
|
||||||
thingEvents.Spawned += NotifySlotGroupCellCountChanged;
|
thingEvents.Spawned += NotifySlotGroupCellCountChanged;
|
||||||
|
thingEvents.Spawned += StorageSettingsPatches.InitializeSlotGroupParent;
|
||||||
thingEvents.DeSpawned += NotifySlotGroupCellCountChanged;
|
thingEvents.DeSpawned += NotifySlotGroupCellCountChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,10 +133,20 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
|||||||
=> slotGroupParent.GetSlotGroup()?.NotifyCellCountChanged();
|
=> slotGroupParent.GetSlotGroup()?.NotifyCellCountChanged();
|
||||||
|
|
||||||
public static void TryNotifyReceivedThing(Thing thing, Map map, in IntVec3 cell)
|
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)
|
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)
|
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||||
@@ -138,7 +155,7 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
|||||||
bool needAccurateResult, ref IntVec3 closestSlot, ref float closestDistSquared,
|
bool needAccurateResult, ref IntVec3 closestSlot, ref float closestDistSquared,
|
||||||
ref StoragePriority foundPriority)
|
ref StoragePriority foundPriority)
|
||||||
{
|
{
|
||||||
if (slotGroup == null || !slotGroup.parent.Accepts(t))
|
if (slotGroup == null || !slotGroup.parent.Accepts(t) || !CapacityAllows(slotGroup, t))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var thingPosition = t.SpawnedOrAnyParentSpawned ? t.PositionHeld : carrier.PositionHeld;
|
var thingPosition = t.SpawnedOrAnyParentSpawned ? t.PositionHeld : carrier.PositionHeld;
|
||||||
@@ -150,7 +167,7 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
StartOfDistrictLoop:
|
StartOfDistrictLoop:
|
||||||
var districts = slotGroup.Districts();
|
var districts = GetDistricts(slotGroup);
|
||||||
|
|
||||||
for (var districtIndex = 0; districtIndex < districts.Length; districtIndex++)
|
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
|
public sealed class TryFindBestBetterStoreCellForPatch : FishPrepatch
|
||||||
@@ -210,6 +247,11 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
|||||||
foundCell = IntVec3.Invalid;
|
foundCell = IntVec3.Invalid;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if STORAGE_GROUP_DEBUG
|
||||||
|
if (!Debug.LoggedOnce && !(Current.Game?.tickManager?.Paused ?? true))
|
||||||
|
Debug.LogStuff(listInPriorityOrder);
|
||||||
|
#endif
|
||||||
|
|
||||||
var foundPriority = currentPriority;
|
var foundPriority = currentPriority;
|
||||||
var closestDistSquared = (float)int.MaxValue;
|
var closestDistSquared = (float)int.MaxValue;
|
||||||
@@ -232,8 +274,38 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
|||||||
|
|
||||||
while (groupsOfPriorityCount-- > 0)
|
while (groupsOfPriorityCount-- > 0)
|
||||||
{
|
{
|
||||||
StoreUtility.TryFindBestBetterStoreCellForWorker(t, carrier, map, faction, listInPriorityOrder[i++],
|
var slotGroup = listInPriorityOrder[i++];
|
||||||
needAccurateResult, ref closestSlot, ref closestDistSquared, ref foundPriority);
|
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
|
// remove for any storage instead of only valid storage to prevent further haul attempts until the next
|
||||||
// automatic ListerHaulablesTick cycle
|
// 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
|
public sealed class GetSlotGroupPatch : FishPrepatch
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ public sealed class JobGiver_HaulOptimization : ClassWithFishPatches
|
|||||||
|
|
||||||
public static List<Thing> SortedThingsPotentiallyNeedingHauling(List<Thing> things, Pawn pawn)
|
public static List<Thing> SortedThingsPotentiallyNeedingHauling(List<Thing> things, Pawn pawn)
|
||||||
{
|
{
|
||||||
_comparer.rootCell = pawn.Position;
|
((ThingPositionComparer)_comparer.Target).rootCell = pawn.Position;
|
||||||
things.Sort(_comparer);
|
things.Sort(_comparer);
|
||||||
return things;
|
return things;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static WorkGiver_DoBillOptimization.ThingPositionComparer _comparer = new();
|
private static Comparison<Thing> _comparer = new ThingPositionComparer().Compare;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +172,7 @@ public sealed class WorkGiver_DoBillPrepatches : ClassWithFishPrepatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly WorkGiver_DoBillOptimization.ThingPositionComparer _insertAtCorrectPositionComparer
|
private static readonly ThingPositionComparer _insertAtCorrectPositionComparer
|
||||||
= new();
|
= new();
|
||||||
|
|
||||||
public static void ActualLoop(List<Thing> list, Region r, Pawn pawn, Predicate<Thing> baseValidator,
|
public static void ActualLoop(List<Thing> list, Region r, Pawn pawn, Predicate<Thing> baseValidator,
|
||||||
@@ -599,16 +599,6 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ThingPositionComparer : IComparer<Thing>
|
|
||||||
{
|
|
||||||
public IntVec3 rootCell;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public int Compare(Thing x, Thing y)
|
|
||||||
=> (x.Position - rootCell).LengthHorizontalSquared.CompareTo(
|
|
||||||
(y.Position - rootCell).LengthHorizontalSquared);
|
|
||||||
}
|
|
||||||
|
|
||||||
public record struct RecipeIngredientCacheValue
|
public record struct RecipeIngredientCacheValue
|
||||||
{
|
{
|
||||||
public IngredientCacheList? thingDefs;
|
public IngredientCacheList? thingDefs;
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ public sealed class WorkGiver_HaulOptimization : ClassWithFishPatches
|
|||||||
|
|
||||||
private static void Sort(List<Thing> list, Pawn pawn)
|
private static void Sort(List<Thing> list, Pawn pawn)
|
||||||
{
|
{
|
||||||
_comparer.rootCell = pawn.Position;
|
((ThingPositionComparer)_comparer.Target).rootCell = pawn.Position;
|
||||||
list.Sort(_comparer);
|
list.Sort(_comparer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static WorkGiver_DoBillOptimization.ThingPositionComparer _comparer = new();
|
private static Comparison<Thing> _comparer = new ThingPositionComparer().Compare;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,6 +100,15 @@ public static class ParallelNoAlloc
|
|||||||
return monitorObject;
|
return monitorObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool HasAnyRegisteredWorkers(object? monitorObject)
|
||||||
|
{
|
||||||
|
Guard.IsNotNull(monitorObject);
|
||||||
|
|
||||||
|
return monitorObject is MonitorObject.Group typedMonitorObject
|
||||||
|
? typedMonitorObject.Subscribers.Length > 0
|
||||||
|
: ThrowHelper.ThrowArgumentException<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
public static void DeregisterBackgroundWaitingWorker(object monitorObject, Action action)
|
public static void DeregisterBackgroundWaitingWorker(object monitorObject, Action action)
|
||||||
{
|
{
|
||||||
Guard.IsNotNull(action);
|
Guard.IsNotNull(action);
|
||||||
@@ -140,6 +149,8 @@ public static class ParallelNoAlloc
|
|||||||
typedMonitorObject.Pulse();
|
typedMonitorObject.Pulse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void ClearAll() => _groupWorkers.Clear();
|
||||||
|
|
||||||
private abstract class Worker : IFishPoolable
|
private abstract class Worker : IFishPoolable
|
||||||
{
|
{
|
||||||
public Thread Thread { get; private set; }
|
public Thread Thread { get; private set; }
|
||||||
@@ -217,13 +228,10 @@ public static class ParallelNoAlloc
|
|||||||
|
|
||||||
public void Invoke(Action action)
|
public void Invoke(Action action)
|
||||||
{
|
{
|
||||||
Guard.IsNotAssignableToType<MonitorObject.Group>(MonitorObject);
|
|
||||||
|
|
||||||
if (Pulsed)
|
if (Pulsed)
|
||||||
Spin();
|
Spin();
|
||||||
|
|
||||||
Action = action;
|
Action = action;
|
||||||
|
|
||||||
MonitorObject.Pulse();
|
MonitorObject.Pulse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public abstract class FishPatch : SingletonFactory<FishPatch>, IExposable, IHasD
|
|||||||
: Description
|
: Description
|
||||||
+ $"{(Description?.EndsWith(".") ?? true ? "" : ".")} Disabled for compatibility with {
|
+ $"{(Description?.EndsWith(".") ?? true ? "" : ".")} Disabled for compatibility with {
|
||||||
IncompatibleModIDs.Select(static id => ActiveMods.TryGetModMetaData(id)?.Name)
|
IncompatibleModIDs.Select(static id => ActiveMods.TryGetModMetaData(id)?.Name)
|
||||||
.Where(static id => id != null).ToCommaList(true)}";
|
.Where(Is.NotNull).ToCommaList(true)}";
|
||||||
|
|
||||||
public virtual Delegate? TargetMethodGroup => null;
|
public virtual Delegate? TargetMethodGroup => null;
|
||||||
public virtual IEnumerable<Delegate>? TargetMethodGroups => null;
|
public virtual IEnumerable<Delegate>? TargetMethodGroups => null;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
<Configurations>v1.4</Configurations>
|
<Configurations>v1.4</Configurations>
|
||||||
<Version>0.3.999.8</Version>
|
<Version>0.3.999.9</Version>
|
||||||
<NoWarn>
|
<NoWarn>
|
||||||
1701;1702;IL2121;CS8500;CS9082;CA1000;CA1002;CA1003;CA1707;CA1010;CA1715;CA1716;CA1724;CA1031;CA1033;
|
1701;1702;IL2121;CS8500;CS9082;CA1000;CA1002;CA1003;CA1707;CA1010;CA1715;CA1716;CA1724;CA1031;CA1033;
|
||||||
CA1034;CA1043;CA1051;CA1305;CA1310;CA1710;CA1711;CA1810;CA1812;CA1819;CA1851;CA2002;CA5394;CA1725;CA1062
|
CA1034;CA1043;CA1051;CA1305;CA1310;CA1710;CA1711;CA1810;CA1812;CA1819;CA1851;CA2002;CA5394;CA1725;CA1062
|
||||||
|
|||||||
@@ -151,8 +151,8 @@ public sealed class PerformanceFishMod : Mod
|
|||||||
public static void LogPatchCount()
|
public static void LogPatchCount()
|
||||||
{
|
{
|
||||||
var allFishPatches = AllPatchClasses!.SelectMany(static patchClass => patchClass.Patches.All.Values).ToList();
|
var allFishPatches = AllPatchClasses!.SelectMany(static patchClass => patchClass.Patches.All.Values).ToList();
|
||||||
var allFishPrepatches
|
var allFishPrepatches = AllPrepatchClasses!.SelectMany(static patchClass => patchClass.Patches.All.Values)
|
||||||
= AllPrepatchClasses!.SelectMany(static patchClass => patchClass.Patches.All.Values).ToList();
|
.ToList();
|
||||||
|
|
||||||
var prefixCount = SumFor(allFishPatches, static fishPatch => fishPatch.PrefixMethodInfo);
|
var prefixCount = SumFor(allFishPatches, static fishPatch => fishPatch.PrefixMethodInfo);
|
||||||
var postfixCount = SumFor(allFishPatches, static fishPatch => fishPatch.PostfixMethodInfo);
|
var postfixCount = SumFor(allFishPatches, static fishPatch => fishPatch.PostfixMethodInfo);
|
||||||
@@ -174,10 +174,13 @@ public sealed class PerformanceFishMod : Mod
|
|||||||
(prepatcherClassPatchCount == 1 ? "" : "es")}. That's a total of {prefixCount + postfixCount
|
(prepatcherClassPatchCount == 1 ? "" : "es")}. That's a total of {prefixCount + postfixCount
|
||||||
+ transpilerCount + prepatcherPrefixCount + prepatcherPostfixCount
|
+ transpilerCount + prepatcherPrefixCount + prepatcherPostfixCount
|
||||||
+ prepatcherTranspilerCount + prepatcherClassPatchCount} patches. Amazing!\n\n"
|
+ prepatcherTranspilerCount + prepatcherClassPatchCount} patches. Amazing!\n\n"
|
||||||
+ $"The following methods were patched:\n{string.Join("\n", allFishPatches.SelectMany(static fishPatch
|
+ $"The following methods were patched:\n{string.Join("\n",
|
||||||
=> fishPatch.TargetMethodInfos).Concat(allFishPrepatches.Select(static fishPrepatch
|
allFishPatches
|
||||||
=> (fishPrepatch as FishPrepatch)?.TargetMethodBase)).Where(static method
|
.SelectMany(static fishPatch => fishPatch.TargetMethodInfos)
|
||||||
=> method != null).Select(static method => method.FullDescription()))}\n"
|
.Concat(allFishPrepatches
|
||||||
|
.Select(static fishPrepatch => (fishPrepatch as FishPrepatch)?.TargetMethodBase))
|
||||||
|
.Where(Is.NotNull)
|
||||||
|
.Select(static method => method.FullDescription()))}\n"
|
||||||
+ $"===================================================================================");
|
+ $"===================================================================================");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,11 @@ public static class PrepatcherFields
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static extern GasGridOptimization.ParallelGasGrid[] ParallelGasGrids(this GasGrid gasGrid);
|
public static extern GasGridOptimization.ParallelGasGrid[] ParallelGasGrids(this GasGrid gasGrid);
|
||||||
|
|
||||||
|
[PrepatcherField]
|
||||||
|
[ValueInitializer(nameof(CreateGroupMonitorObject))]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static extern object GasGridMonitorObject(this Map map);
|
||||||
|
|
||||||
[PrepatcherField]
|
[PrepatcherField]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static extern ref Texture2D? ExpandingIconCache(this WorldObject worldObject);
|
public static extern ref Texture2D? ExpandingIconCache(this WorldObject worldObject);
|
||||||
@@ -151,6 +156,9 @@ public static class PrepatcherFields
|
|||||||
public static Listers.Haulables.Cache CreateListerHaulablesCache() => new();
|
public static Listers.Haulables.Cache CreateListerHaulablesCache() => new();
|
||||||
public static HaulDestinationManagerCache CreateHaulDestinationManagerCache() => new();
|
public static HaulDestinationManagerCache CreateHaulDestinationManagerCache() => new();
|
||||||
|
|
||||||
|
public static object CreateGroupMonitorObject()
|
||||||
|
=> ParallelNoAlloc.RegisterBackgroundWaitingWorkers(Array.Empty<Action>());
|
||||||
|
|
||||||
public static GasGridOptimization.ParallelGasGrid[] CreateParallelGasGridArray(GasGrid gasGrid)
|
public static GasGridOptimization.ParallelGasGrid[] CreateParallelGasGridArray(GasGrid gasGrid)
|
||||||
{
|
{
|
||||||
DefDatabase<GasDef>.SetIndices();
|
DefDatabase<GasDef>.SetIndices();
|
||||||
@@ -185,6 +193,17 @@ public static class PrepatcherFields
|
|||||||
for (var i = 0; i < grids.Length; i++)
|
for (var i = 0; i < grids.Length; i++)
|
||||||
grids[i] = new(map, gasDefs[i]);
|
grids[i] = new(map, gasDefs[i]);
|
||||||
|
|
||||||
|
if (FishSettings.ThreadingEnabled)
|
||||||
|
{
|
||||||
|
var monitorObject = map.GasGridMonitorObject();
|
||||||
|
|
||||||
|
for (var i = 1; i < grids.Length; i++)
|
||||||
|
{
|
||||||
|
var grid = grids[i];
|
||||||
|
ParallelNoAlloc.RegisterBackgroundWaitingWorker(monitorObject, () => grid.Tick());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return grids;
|
return grids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using Mono.Cecil.Rocks;
|
using Mono.Cecil.Rocks;
|
||||||
@@ -75,8 +76,7 @@ public abstract class FishPrepatch : FishPrepatchBase
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error($"Exception while applying transpiler for {GetType().FullName}\n{e}\n{
|
Log.Error($"Exception while applying transpiler for {GetType().FullName}\n{e}\n{new StackTrace(true)}");
|
||||||
Environment.StackTrace /*StackTraceUtility.ExtractStackTrace()*/}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var patchVariables = new PatchVariables();
|
var patchVariables = new PatchVariables();
|
||||||
@@ -89,8 +89,7 @@ public abstract class FishPrepatch : FishPrepatchBase
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error($"Exception while applying prefix for {GetType().FullName}\n{e}\n{
|
Log.Error($"Exception while applying prefix for {GetType().FullName}\n{e}\n{new StackTrace(true)}");
|
||||||
Environment.StackTrace /*StackTraceUtility.ExtractStackTrace()*/}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +101,7 @@ public abstract class FishPrepatch : FishPrepatchBase
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error($"Exception while applying postfix for {GetType().FullName}\n{e}\n{
|
Log.Error($"Exception while applying postfix for {GetType().FullName}\n{e}\n{new StackTrace(true)}");
|
||||||
Environment.StackTrace /*StackTraceUtility.ExtractStackTrace()*/}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public abstract class FishPrepatchBase : SingletonFactory<FishPrepatchBase>, IEx
|
|||||||
: Description
|
: Description
|
||||||
+ $"{(Description?.EndsWith(".") ?? true ? "" : ".")} Disabled for compatibility with {
|
+ $"{(Description?.EndsWith(".") ?? true ? "" : ".")} Disabled for compatibility with {
|
||||||
IncompatibleModIDs.Select(static id => ActiveMods.TryGetModMetaData(id)?.Name)
|
IncompatibleModIDs.Select(static id => ActiveMods.TryGetModMetaData(id)?.Name)
|
||||||
.Where(static id => id != null).ToCommaList(true)}";
|
.Where(Is.NotNull).ToCommaList(true)}";
|
||||||
|
|
||||||
public virtual string? Name => null;
|
public virtual string? Name => null;
|
||||||
|
|
||||||
|
|||||||
@@ -74,9 +74,11 @@ public unsafe struct FishStash : IEquatable<FishStash>
|
|||||||
|
|
||||||
internal void InitializeActivePrepatchIDs()
|
internal void InitializeActivePrepatchIDs()
|
||||||
{
|
{
|
||||||
var activePrepatches = PerformanceFishMod.AllPrepatchClasses!.SelectMany(static patchClass
|
var activePrepatches = PerformanceFishMod.AllPrepatchClasses!
|
||||||
=> patchClass.Patches.Where(static patch
|
.SelectMany(static patchClass => patchClass.Patches
|
||||||
=> patch.Enabled).Select(static patch => patch.IDNumber)).ToArray();
|
.Where(static patch => patch.Enabled)
|
||||||
|
.Select(static patch => patch.IDNumber))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
var activePrepatchCount = activePrepatches.Length;
|
var activePrepatchCount = activePrepatches.Length;
|
||||||
_activePrepatchCount = activePrepatchCount;
|
_activePrepatchCount = activePrepatchCount;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using Mono.Collections.Generic;
|
|||||||
using nuget::JetBrains.Annotations;
|
using nuget::JetBrains.Annotations;
|
||||||
using PerformanceFish.ModCompatibility;
|
using PerformanceFish.ModCompatibility;
|
||||||
using CollectionExtensions = PerformanceFish.Utility.CollectionExtensions;
|
using CollectionExtensions = PerformanceFish.Utility.CollectionExtensions;
|
||||||
|
using FieldAttributes = Mono.Cecil.FieldAttributes;
|
||||||
using MethodBody = Mono.Cecil.Cil.MethodBody;
|
using MethodBody = Mono.Cecil.Cil.MethodBody;
|
||||||
|
|
||||||
namespace PerformanceFish.Prepatching;
|
namespace PerformanceFish.Prepatching;
|
||||||
@@ -29,6 +30,20 @@ public static class PrepatchManager
|
|||||||
ThrowHelper.ThrowInvalidOperationException("Tried running free patches a second time. This should "
|
ThrowHelper.ThrowInvalidOperationException("Tried running free patches a second time. This should "
|
||||||
+ "never happen. Cancelling now to avoid further issues.");
|
+ "never happen. Cancelling now to avoid further issues.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var gasFields = module.GetTypeDefinition(typeof(Gas))!.Fields;
|
||||||
|
if (gasFields.Any(static field => field.Name == VERIFICATION_FIELD_NAME))
|
||||||
|
{ // TODO: fix unity logger breaking when getting here
|
||||||
|
UnityEngine.Debug.Log("Detected second prepatch attempt. This is known to happen after using "
|
||||||
|
+ "Prepatcher's mod manager. Cancelling and restarting now to prevent issues");
|
||||||
|
GenCommandLine.Restart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gasFields.Add(new(VERIFICATION_FIELD_NAME,
|
||||||
|
FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.Literal,
|
||||||
|
module.TypeSystem.Byte));
|
||||||
|
}
|
||||||
|
|
||||||
Log.Message(LogPatches.PERFORMANCE_FISH_WELCOME_MESSAGE);
|
Log.Message(LogPatches.PERFORMANCE_FISH_WELCOME_MESSAGE);
|
||||||
if (!ActiveMods.Prepatcher)
|
if (!ActiveMods.Prepatcher)
|
||||||
@@ -84,7 +99,9 @@ public static class PrepatchManager
|
|||||||
Verse.Log.Message($"Performance Fish finished applying prepatches in {
|
Verse.Log.Message($"Performance Fish finished applying prepatches in {
|
||||||
stopwatch.ElapsedSecondsAccurate():N7} seconds");
|
stopwatch.ElapsedSecondsAccurate():N7} seconds");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string VERIFICATION_FIELD_NAME = "PerformanceFishS1ngl3Pr3p4tchV3r1f1c4t10nF13ld";
|
||||||
|
|
||||||
// private static readonly FieldInfo? _monoAssemblyField = AccessTools.Field(typeof(Assembly), "_mono_assembly");
|
// private static readonly FieldInfo? _monoAssemblyField = AccessTools.Field(typeof(Assembly), "_mono_assembly");
|
||||||
//
|
//
|
||||||
// internal static unsafe void SetReflectionOnly(Assembly asm, bool value, out bool previousValue)
|
// internal static unsafe void SetReflectionOnly(Assembly asm, bool value, out bool previousValue)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -745,7 +746,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
cache.Task = _fieldSetterFailureTask;
|
cache.Task = _fieldSetterFailureTask;
|
||||||
Log.Error($"FieldInfo.SetValue has been called on '{
|
Log.Error($"FieldInfo.SetValue has been called on '{
|
||||||
info.FullDescription()}'. This is a const that gets copied at compile time. An attempt at "
|
info.FullDescription()}'. This is a const that gets copied at compile time. An attempt at "
|
||||||
+ $"setting a value will always fail.");
|
+ $"setting a value will always fail.\n{new StackTrace(true)}");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -778,7 +779,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
{
|
{
|
||||||
Log.Error($"PerformanceFish failed to generate an optimized delegate for {
|
Log.Error($"PerformanceFish failed to generate an optimized delegate for {
|
||||||
info.FullDescription()}. Reverting to default behaviour instead.\n{ex}\n{
|
info.FullDescription()}. Reverting to default behaviour instead.\n{ex}\n{
|
||||||
Environment.StackTrace}");
|
new StackTrace(true)}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -821,8 +822,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error($"PerformanceFish failed to generate an optimized delegate for {
|
Log.Error($"PerformanceFish failed to generate an optimized delegate for {
|
||||||
info.FullDescription()}. Reverting to default behaviour instead.\n{ex}\n{
|
info.FullDescription()}. Reverting to default behaviour instead.\n{ex}\n{new StackTrace(true)}");
|
||||||
Environment.StackTrace}");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -960,7 +960,7 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
|||||||
{
|
{
|
||||||
Log.Warning($"PerformanceFish failed to generate an optimized delegate for {
|
Log.Warning($"PerformanceFish failed to generate an optimized delegate for {
|
||||||
Reflection.FullDescription(methodBase)}. Reverting to default behaviour instead.\n{ex}\n{
|
Reflection.FullDescription(methodBase)}. Reverting to default behaviour instead.\n{ex}\n{
|
||||||
Environment.StackTrace}");
|
new StackTrace(true)}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// https://stackoverflow.com/a/18363848
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace PerformanceFish.Utility;
|
||||||
|
|
||||||
|
public static class Is
|
||||||
|
{
|
||||||
|
public static bool Null<T>(T? value) where T : class => value == null;
|
||||||
|
|
||||||
|
public static bool NotNull<T>([NotNullWhen(true)] T? value) where T : class => value != null;
|
||||||
|
|
||||||
|
public static bool Null<T>(T? nullableValue) where T : struct => !nullableValue.HasValue;
|
||||||
|
|
||||||
|
public static bool NotNull<T>([NotNullWhen(true)] T? nullableValue) where T : struct => nullableValue.HasValue;
|
||||||
|
|
||||||
|
public static bool HasValue<T>([NotNullWhen(true)] T? nullableValue) where T : struct => nullableValue.HasValue;
|
||||||
|
|
||||||
|
public static bool HasNoValue<T>(T? nullableValue) where T : struct => !nullableValue.HasValue;
|
||||||
|
}
|
||||||
@@ -29,9 +29,29 @@ public static class MiscExtensions
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool IsItem(this Thing thing) => thing.def.category == ThingCategory.Item;
|
public static bool IsItem(this Thing thing) => thing.def.category == ThingCategory.Item;
|
||||||
|
|
||||||
|
public static StorageSettings? TryGetGroupStoreSettings(this ISlotGroupParent slotGroupParent)
|
||||||
|
=> slotGroupParent.TryGetStorageGroup()?.GetStoreSettings();
|
||||||
|
|
||||||
|
public static StorageGroup? TryGetStorageGroup(this SlotGroup slotGroup) => slotGroup.parent.TryGetStorageGroup();
|
||||||
|
|
||||||
|
public static StorageGroup? TryGetStorageGroup(this ISlotGroupParent slotGroupParent)
|
||||||
|
=> slotGroupParent is IStorageGroupMember groupMember ? groupMember.Group : null;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool Accepts(this StorageGroup storageGroup, Thing t)
|
||||||
|
=> storageGroup.GetStoreSettings().AllowedToAccept(t);
|
||||||
|
|
||||||
public static ISlotGroupParent? GetSlotGroupParent(this in IntVec3 position, Map map)
|
public static ISlotGroupParent? GetSlotGroupParent(this in IntVec3 position, Map map)
|
||||||
=> map.haulDestinationManager.SlotGroupAt(position)?.parent;
|
=> map.haulDestinationManager.SlotGroupAt(position)?.parent;
|
||||||
|
|
||||||
|
public static StorageSettings? GetThingStoreSettings(this SlotGroup slotGroup)
|
||||||
|
=> slotGroup.parent.GetThingStoreSettings();
|
||||||
|
|
||||||
|
public static StorageSettings? GetThingStoreSettings(this ISlotGroupParent slotGroupParent)
|
||||||
|
=> slotGroupParent is IStorageGroupMember groupMember
|
||||||
|
? groupMember.ThingStoreSettings
|
||||||
|
: slotGroupParent.GetStoreSettings();
|
||||||
|
|
||||||
public static ISlotGroupParent? StoringSlotGroupParent(this Thing thing)
|
public static ISlotGroupParent? StoringSlotGroupParent(this Thing thing)
|
||||||
=> thing.Position.GetSlotGroupParent(thing.GetMap());
|
=> thing.Position.GetSlotGroupParent(thing.GetMap());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2023 bradson
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
namespace PerformanceFish.Utility;
|
||||||
|
|
||||||
|
public sealed class ThingPositionComparer : IComparer<Thing>
|
||||||
|
{
|
||||||
|
public IntVec3 rootCell;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int Compare(Thing x, Thing y)
|
||||||
|
=> (x.Position - rootCell).LengthHorizontalSquared.CompareTo(
|
||||||
|
(y.Position - rootCell).LengthHorizontalSquared);
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ public sealed class UtilityPatches : ClassWithFishPatches
|
|||||||
public static void Postfix()
|
public static void Postfix()
|
||||||
{
|
{
|
||||||
Cache.Utility.Clear();
|
Cache.Utility.Clear();
|
||||||
|
ParallelNoAlloc.ClearAll();
|
||||||
foreach (var patch in PerformanceFishMod.AllPatchClasses!)
|
foreach (var patch in PerformanceFishMod.AllPatchClasses!)
|
||||||
{
|
{
|
||||||
if (patch.RequiresLoadedGameForPatching)
|
if (patch.RequiresLoadedGameForPatching)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
using PerformanceFish.Prepatching;
|
using PerformanceFish.Prepatching;
|
||||||
|
|
||||||
namespace PerformanceFish;
|
namespace PerformanceFish;
|
||||||
@@ -30,8 +31,7 @@ public sealed class UtilityPrepatches : ClassWithFishPrepatches
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error($"Exception caught within OnAssembliesLoaded:\n{e}\n{
|
Log.Error($"Exception caught within OnAssembliesLoaded:\n{e}\n{new StackTrace(true)}");
|
||||||
Environment.StackTrace /*StackTraceUtility.ExtractStackTrace()*/}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user