diff --git a/.gitignore b/.gitignore
index a34df41..b5ff8a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -481,3 +481,6 @@ $RECYCLE.BIN/
# Vim temporary swap files
*.swp
+
+# Cache folders used in disk caching
+Cache/*
\ No newline at end of file
diff --git a/1.5/Assemblies/PerformanceFish.dll b/1.5/Assemblies/PerformanceFish.dll
index 24304e8..eacfdc0 100644
Binary files a/1.5/Assemblies/PerformanceFish.dll and b/1.5/Assemblies/PerformanceFish.dll differ
diff --git a/1.5/Assemblies/PerformanceFish.pdb b/1.5/Assemblies/PerformanceFish.pdb
index 312e985..4771ea9 100644
Binary files a/1.5/Assemblies/PerformanceFish.pdb and b/1.5/Assemblies/PerformanceFish.pdb differ
diff --git a/1.6/Assemblies/PerformanceFish.dll b/1.6/Assemblies/PerformanceFish.dll
new file mode 100644
index 0000000..1798725
Binary files /dev/null and b/1.6/Assemblies/PerformanceFish.dll differ
diff --git a/1.6/Assemblies/PerformanceFish.pdb b/1.6/Assemblies/PerformanceFish.pdb
new file mode 100644
index 0000000..bdc70d0
Binary files /dev/null and b/1.6/Assemblies/PerformanceFish.pdb differ
diff --git a/About/About.xml b/About/About.xml
index 4817d54..78aa9e3 100644
--- a/About/About.xml
+++ b/About/About.xml
@@ -5,6 +5,7 @@
1.4
1.5
+ 1.6
bradson
PerformanceFish/ModIcon
@@ -56,4 +57,4 @@
vanillaexpanded.storytellers.rick
download more tps
-
\ No newline at end of file
+
diff --git a/Source/PerformanceFish/1.5.csproj b/Source/PerformanceFish/1.5.csproj
index baf5a13..7379b8e 100644
--- a/Source/PerformanceFish/1.5.csproj
+++ b/Source/PerformanceFish/1.5.csproj
@@ -3,6 +3,8 @@
..\..\1.5\Assemblies\
Copyright (c) 2024 Bradson
+ $(MSBuildThisFileDirectory)..\..\..\Fishery
+ $(MSBuildThisFileDirectory)..\..\..\Fishery-clean
DEBUG;V1_5
@@ -14,7 +16,7 @@
- ..\..\..\Fishery\1.5\Assemblies\1Fishery.dll
+ $(FisheryRepoRoot)\1.5\Assemblies\1Fishery.dll
False
diff --git a/Source/PerformanceFish/1.6.csproj b/Source/PerformanceFish/1.6.csproj
new file mode 100644
index 0000000..1478547
--- /dev/null
+++ b/Source/PerformanceFish/1.6.csproj
@@ -0,0 +1,23 @@
+
+
+
+ ..\..\1.6\Assemblies\
+ Copyright (c) 2026 Bradson
+ $(MSBuildThisFileDirectory)..\..\..\Fishery
+ $(MSBuildThisFileDirectory)..\..\..\Fishery-clean
+
+
+ DEBUG;V1_5;V1_6
+
+
+ V1_5;V1_6
+
+
+
+
+
+ $(FisheryRepoRoot)\1.6\Assemblies\1Fishery.dll
+ False
+
+
+
diff --git a/Source/PerformanceFish/Cache/BitCellGrid.cs b/Source/PerformanceFish/Cache/BitCellGrid.cs
index 9e99ea6..1027cc4 100644
--- a/Source/PerformanceFish/Cache/BitCellGrid.cs
+++ b/Source/PerformanceFish/Cache/BitCellGrid.cs
@@ -7,13 +7,15 @@ namespace PerformanceFish.Cache;
public sealed class BitCellGrid
{
+ private readonly Map _map;
private long[] _innerArray;
private CellIndices _cellIndices;
public BitCellGrid(Map map)
{
- _innerArray = null!;
- _cellIndices = null!;
+ _map = map;
+ _innerArray = Array.Empty();
+ _cellIndices = default;
map.InvokeWhenCellIndicesReady(Initialize);
}
@@ -23,12 +25,26 @@ public sealed class BitCellGrid
_innerArray = new long[(Math.Max(0, _cellIndices.NumGridCells - 1) >> 6) + 1];
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void EnsureInitialized()
+ {
+ if (_cellIndices.NumGridCells != _map.cellIndices.NumGridCells)
+ Initialize(_map);
+ }
+
public bool this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_innerArray[index >> 6] & (1L << (index & 63))) != 0L;
+ get
+ {
+ EnsureInitialized();
+ ValidateIndex(index);
+ return (_innerArray[index >> 6] & (1L << (index & 63))) != 0L;
+ }
set
{
+ EnsureInitialized();
+ ValidateIndex(index);
ref var bucket = ref _innerArray[index >> 6];
bucket ^= (-(long)value.AsInt() ^ bucket) & (1L << (index & 63));
}
@@ -37,7 +53,24 @@ public sealed class BitCellGrid
public bool this[in IntVec3 c]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => this[c.CellToIndex(_cellIndices)];
- set => this[c.CellToIndex(_cellIndices)] = value;
+ get
+ {
+ EnsureInitialized();
+ return this[c.CellToIndex(_cellIndices)];
+ }
+ set
+ {
+ EnsureInitialized();
+ this[c.CellToIndex(_cellIndices)] = value;
+ }
}
-}
\ No newline at end of file
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private void ValidateIndex(int index)
+ {
+ if ((uint)index >= (uint)_cellIndices.NumGridCells)
+ {
+ throw new IndexOutOfRangeException($"BitCellGrid index {index} out of bounds for {_cellIndices.NumGridCells} cells on map size {_map.Size}.");
+ }
+ }
+}
diff --git a/Source/PerformanceFish/Cache/CellGrid.cs b/Source/PerformanceFish/Cache/CellGrid.cs
index d83ad3f..9d964e5 100644
--- a/Source/PerformanceFish/Cache/CellGrid.cs
+++ b/Source/PerformanceFish/Cache/CellGrid.cs
@@ -9,38 +9,60 @@ namespace PerformanceFish.Cache;
public sealed class CellGrid
{
+ private readonly Map _map;
private T[] _innerArray;
private CellIndices _cellIndices;
public CellGrid(Map map)
{
- _innerArray = null!;
- _cellIndices = null!;
+ _map = map;
+ _innerArray = Array.Empty();
+ _cellIndices = default;
map.InvokeWhenCellIndicesReady(Initialize);
}
- [MemberNotNull(nameof(_innerArray), nameof(_cellIndices))]
+ [MemberNotNull(nameof(_innerArray))]
private void Initialize(Map map)
{
_cellIndices = map.cellIndices;
_innerArray = new T[_cellIndices.NumGridCells];
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void EnsureInitialized()
+ {
+ if (_cellIndices.NumGridCells != _map.cellIndices.NumGridCells)
+ Initialize(_map);
+ }
+
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => ref _innerArray[index];
+ get
+ {
+ EnsureInitialized();
+ return ref _innerArray[index];
+ }
}
public ref T this[in IntVec3 c]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => ref _innerArray[c.CellToIndex(_cellIndices)];
+ get
+ {
+ EnsureInitialized();
+ var index = c.CellToIndex(_cellIndices);
+ return ref _innerArray[index];
+ }
}
public ref T this[CellIndex cellIndex]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => ref _innerArray[cellIndex.Value];
+ get
+ {
+ EnsureInitialized();
+ return ref _innerArray[cellIndex.Value];
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Source/PerformanceFish/CellIndex.cs b/Source/PerformanceFish/CellIndex.cs
index 731b6c4..e2f544f 100644
--- a/Source/PerformanceFish/CellIndex.cs
+++ b/Source/PerformanceFish/CellIndex.cs
@@ -10,20 +10,25 @@ public record struct CellIndex(int Value)
public int Value = Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public CellIndex(IntVec3 cell, Map map) : this(cell, map.cellIndices.mapSizeX) {}
+ public CellIndex(IntVec3 cell, Map map) : this(cell, map.Size.x) {}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CellIndex(IntVec3 cell, int mapSizeX) : this(cell.CellToIndex(mapSizeX)) {}
- public IntVec3 ToCell(Map map) => ToCell(map.cellIndices.mapSizeX);
+ public IntVec3 ToCell(Map map) => ToCell(map.Size.x);
public IntVec3 ToCell(int mapSizeX) => CellIndicesUtility.IndexToCell(Value, mapSizeX);
public List GetThingList(Map map) => map.thingGrid.thingGrid[Value];
- public bool IsFogged(Map map) => map.fogGrid.fogGrid[Value];
+ public bool IsFogged(Map map)
+#if V1_6
+ => map.fogGrid.fogGrid.IsSet(Value);
+#else
+ => map.fogGrid.fogGrid[Value];
+#endif
public float GetSnowDepth(Map map) => map.snowGrid.depthGrid[Value];
public Building? GetEdifice(Map map) => map.edificeGrid[Value];
-}
\ No newline at end of file
+}
diff --git a/Source/PerformanceFish/CompTickOptimization.cs b/Source/PerformanceFish/CompTickOptimization.cs
new file mode 100644
index 0000000..5fceebc
--- /dev/null
+++ b/Source/PerformanceFish/CompTickOptimization.cs
@@ -0,0 +1,353 @@
+// Copyright (c) 2026 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/.
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using PerformanceFish.Prepatching;
+
+namespace PerformanceFish;
+
+public sealed class CompTickOptimization : ClassWithFishPrepatches
+{
+ private static readonly MethodInfo _thingCompTickMethod
+ = AccessTools.DeclaredMethod(typeof(ThingComp), nameof(ThingComp.CompTick));
+
+ private static readonly MethodInfo _thingCompTickIntervalMethod
+ = AccessTools.DeclaredMethod(typeof(ThingComp), nameof(ThingComp.CompTickInterval), [typeof(int)]);
+
+ private static readonly MethodInfo _thingCompTickRareMethod
+ = AccessTools.DeclaredMethod(typeof(ThingComp), nameof(ThingComp.CompTickRare));
+
+ private static readonly MethodInfo _thingCompTickLongMethod
+ = AccessTools.DeclaredMethod(typeof(ThingComp), nameof(ThingComp.CompTickLong));
+
+ private static readonly MethodInfo _hediffCompPostTickMethod
+ = AccessTools.DeclaredMethod(typeof(HediffComp), nameof(HediffComp.CompPostTick), [typeof(float).MakeByRefType()]);
+
+ private static readonly MethodInfo _hediffCompPostTickIntervalMethod = AccessTools.DeclaredMethod(
+ typeof(HediffComp),
+ nameof(HediffComp.CompPostTickInterval),
+ [typeof(float).MakeByRefType(), typeof(int)]);
+
+ private static readonly bool _thingCompTickBaseIsTrivial = IsTrivialVoidMethod(_thingCompTickMethod);
+ private static readonly bool _thingCompTickIntervalBaseIsTrivial = IsTrivialVoidMethod(_thingCompTickIntervalMethod);
+ private static readonly bool _thingCompTickRareBaseIsTrivial = IsTrivialVoidMethod(_thingCompTickRareMethod);
+ private static readonly bool _thingCompTickLongBaseIsTrivial = IsTrivialVoidMethod(_thingCompTickLongMethod);
+ private static readonly bool _hediffCompPostTickBaseIsTrivial = IsTrivialVoidMethod(_hediffCompPostTickMethod);
+ private static readonly bool _hediffCompPostTickIntervalBaseIsTrivial
+ = IsTrivialVoidMethod(_hediffCompPostTickIntervalMethod);
+
+ private static readonly Lazy _canSkipInheritedBaseThingCompTick
+ = new(static () => _thingCompTickBaseIsTrivial && !HasHarmonyPatches(_thingCompTickMethod));
+
+ private static readonly Lazy _canSkipInheritedBaseThingCompTickInterval
+ = new(static () => _thingCompTickIntervalBaseIsTrivial && !HasHarmonyPatches(_thingCompTickIntervalMethod));
+
+ private static readonly Lazy _canSkipInheritedBaseThingCompTickRare
+ = new(static () => _thingCompTickRareBaseIsTrivial && !HasHarmonyPatches(_thingCompTickRareMethod));
+
+ private static readonly Lazy _canSkipInheritedBaseThingCompTickLong
+ = new(static () => _thingCompTickLongBaseIsTrivial && !HasHarmonyPatches(_thingCompTickLongMethod));
+
+ private static readonly Lazy _canSkipInheritedBaseHediffCompPostTick
+ = new(static () => _hediffCompPostTickBaseIsTrivial && !HasHarmonyPatches(_hediffCompPostTickMethod));
+
+ private static readonly Lazy _canSkipInheritedBaseHediffCompPostTickInterval = new(
+ static () => _hediffCompPostTickIntervalBaseIsTrivial && !HasHarmonyPatches(_hediffCompPostTickIntervalMethod));
+
+ private static readonly FishTable _thingCompTickTypes = new()
+ {
+ ValueInitializer = static type => InheritsThingCompTick(type)
+ };
+
+ private static readonly FishTable _thingCompTickIntervalTypes = new()
+ {
+ ValueInitializer = static type => InheritsThingCompTickInterval(type)
+ };
+
+ private static readonly FishTable _thingCompTickRareTypes = new()
+ {
+ ValueInitializer = static type => InheritsThingCompTickRare(type)
+ };
+
+ private static readonly FishTable _thingCompTickLongTypes = new()
+ {
+ ValueInitializer = static type => InheritsThingCompTickLong(type)
+ };
+
+ private static readonly FishTable _hediffCompPostTickTypes = new()
+ {
+ ValueInitializer = static type => InheritsHediffCompPostTick(type)
+ };
+
+ private static readonly FishTable _hediffCompPostTickIntervalTypes = new()
+ {
+ ValueInitializer = static type => InheritsHediffCompPostTickInterval(type)
+ };
+
+ public sealed class ThingTickPatch : FishPrepatch
+ {
+ public override bool DefaultState => false;
+
+ public override string? Description { get; }
+ = "Skips inherited empty ThingComp.CompTick calls when the base method is still unpatched and trivial.";
+
+ public override MethodBase TargetMethodBase { get; }
+ = AccessTools.DeclaredMethod(typeof(ThingWithComps), "Tick");
+
+ public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
+ => ilProcessor.ReplaceBodyWith(ReplacementBody);
+
+ public static void ReplacementBody(ThingWithComps instance)
+ {
+ var comps = instance.AllComps;
+ var count = comps.Count;
+ var canSkipInheritedBase = CanSkipInheritedBaseThingCompTick();
+ for (var i = 0; i < count; i++)
+ {
+ var comp = comps[i];
+ if (!canSkipInheritedBase || !_thingCompTickTypes.GetOrAdd(comp.GetType()))
+ comp.CompTick();
+ }
+ }
+ }
+
+ public sealed class ThingTickIntervalPatch : FishPrepatch
+ {
+ public override bool DefaultState => false;
+
+ public override string? Description { get; }
+ = "Skips inherited empty ThingComp.CompTickInterval calls when the base method is still unpatched and trivial.";
+
+ public override MethodBase TargetMethodBase { get; }
+ = AccessTools.DeclaredMethod(typeof(ThingWithComps), "TickInterval", [typeof(int)]);
+
+ public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
+ => ilProcessor.ReplaceBodyWith(ReplacementBody);
+
+ public static void ReplacementBody(ThingWithComps instance, int delta)
+ {
+ var comps = instance.AllComps;
+ var count = comps.Count;
+ var canSkipInheritedBase = CanSkipInheritedBaseThingCompTickInterval();
+ for (var i = 0; i < count; i++)
+ {
+ var comp = comps[i];
+ if (!canSkipInheritedBase || !_thingCompTickIntervalTypes.GetOrAdd(comp.GetType()))
+ comp.CompTickInterval(delta);
+ }
+ }
+ }
+
+ public sealed class ThingTickRarePatch : FishPrepatch
+ {
+ public override bool DefaultState => false;
+
+ public override string? Description { get; }
+ = "Skips inherited empty ThingComp.CompTickRare calls when the base method is still unpatched and trivial.";
+
+ public override MethodBase TargetMethodBase { get; }
+ = AccessTools.DeclaredMethod(typeof(ThingWithComps), nameof(ThingWithComps.TickRare));
+
+ public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
+ => ilProcessor.ReplaceBodyWith(ReplacementBody);
+
+ public static void ReplacementBody(ThingWithComps instance)
+ {
+ var comps = instance.AllComps;
+ var count = comps.Count;
+ var canSkipInheritedBase = CanSkipInheritedBaseThingCompTickRare();
+ for (var i = 0; i < count; i++)
+ {
+ var comp = comps[i];
+ if (!canSkipInheritedBase || !_thingCompTickRareTypes.GetOrAdd(comp.GetType()))
+ comp.CompTickRare();
+ }
+ }
+ }
+
+ public sealed class ThingTickLongPatch : FishPrepatch
+ {
+ public override bool DefaultState => false;
+
+ public override string? Description { get; }
+ = "Skips inherited empty ThingComp.CompTickLong calls when the base method is still unpatched and trivial.";
+
+ public override MethodBase TargetMethodBase { get; }
+ = AccessTools.DeclaredMethod(typeof(ThingWithComps), nameof(ThingWithComps.TickLong));
+
+ public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
+ => ilProcessor.ReplaceBodyWith(ReplacementBody);
+
+ public static void ReplacementBody(ThingWithComps instance)
+ {
+ var comps = instance.AllComps;
+ var count = comps.Count;
+ var canSkipInheritedBase = CanSkipInheritedBaseThingCompTickLong();
+ for (var i = 0; i < count; i++)
+ {
+ var comp = comps[i];
+ if (!canSkipInheritedBase || !_thingCompTickLongTypes.GetOrAdd(comp.GetType()))
+ comp.CompTickLong();
+ }
+ }
+ }
+
+ public sealed class HediffPostTickPatch : FishPrepatch
+ {
+ public override bool DefaultState => false;
+
+ public override string? Description { get; }
+ = "Skips inherited empty HediffComp.CompPostTick calls when the base method is still unpatched and trivial.";
+
+ public override MethodBase TargetMethodBase { get; }
+ = AccessTools.DeclaredMethod(typeof(HediffWithComps), nameof(HediffWithComps.PostTick));
+
+ public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
+ => ilProcessor.ReplaceBodyWith(ReplacementBody);
+
+ public static void ReplacementBody(HediffWithComps instance)
+ {
+ var comps = instance.comps;
+ if (comps == null)
+ return;
+
+ var count = comps.Count;
+ var severityAdjustment = 0f;
+ var canSkipInheritedBase = CanSkipInheritedBaseHediffCompPostTick();
+
+ for (var i = 0; i < count; i++)
+ {
+ var comp = comps[i];
+ if (!canSkipInheritedBase || !_hediffCompPostTickTypes.GetOrAdd(comp.GetType()))
+ comp.CompPostTick(ref severityAdjustment);
+ }
+
+ if (severityAdjustment != 0f)
+ instance.Severity += severityAdjustment;
+ }
+ }
+
+ public sealed class HediffPostTickIntervalPatch : FishPrepatch
+ {
+ public override bool DefaultState => false;
+
+ public override string? Description { get; }
+ = "Skips inherited empty HediffComp.CompPostTickInterval calls when the base method is still unpatched and trivial.";
+
+ public override MethodBase TargetMethodBase { get; }
+ = AccessTools.DeclaredMethod(typeof(HediffWithComps), nameof(HediffWithComps.PostTickInterval), [typeof(int)]);
+
+ public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
+ => ilProcessor.ReplaceBodyWith(ReplacementBody);
+
+ public static void ReplacementBody(HediffWithComps instance, int delta)
+ {
+ var comps = instance.comps;
+ if (comps == null)
+ return;
+
+ var count = comps.Count;
+ var severityAdjustment = 0f;
+ var canSkipInheritedBase = CanSkipInheritedBaseHediffCompPostTickInterval();
+
+ for (var i = 0; i < count; i++)
+ {
+ var comp = comps[i];
+ if (!canSkipInheritedBase || !_hediffCompPostTickIntervalTypes.GetOrAdd(comp.GetType()))
+ comp.CompPostTickInterval(ref severityAdjustment, delta);
+ }
+
+ if (severityAdjustment != 0f)
+ instance.Severity += severityAdjustment;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool CanSkipInheritedBaseThingCompTick()
+ => _canSkipInheritedBaseThingCompTick.Value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool CanSkipInheritedBaseThingCompTickInterval()
+ => _canSkipInheritedBaseThingCompTickInterval.Value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool CanSkipInheritedBaseThingCompTickRare()
+ => _canSkipInheritedBaseThingCompTickRare.Value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool CanSkipInheritedBaseThingCompTickLong()
+ => _canSkipInheritedBaseThingCompTickLong.Value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool CanSkipInheritedBaseHediffCompPostTick()
+ => _canSkipInheritedBaseHediffCompPostTick.Value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool CanSkipInheritedBaseHediffCompPostTickInterval()
+ => _canSkipInheritedBaseHediffCompPostTickInterval.Value;
+
+ private static bool InheritsThingCompTick(Type type)
+ => AccessTools.Method(type, nameof(ThingComp.CompTick))?.DeclaringType == typeof(ThingComp);
+
+ private static bool InheritsThingCompTickInterval(Type type)
+ => AccessTools.Method(type, nameof(ThingComp.CompTickInterval), [typeof(int)])?.DeclaringType == typeof(ThingComp);
+
+ private static bool InheritsThingCompTickRare(Type type)
+ => AccessTools.Method(type, nameof(ThingComp.CompTickRare))?.DeclaringType == typeof(ThingComp);
+
+ private static bool InheritsThingCompTickLong(Type type)
+ => AccessTools.Method(type, nameof(ThingComp.CompTickLong))?.DeclaringType == typeof(ThingComp);
+
+ private static bool InheritsHediffCompPostTick(Type type)
+ => AccessTools.Method(type, nameof(HediffComp.CompPostTick), [typeof(float).MakeByRefType()])?.DeclaringType
+ == typeof(HediffComp);
+
+ private static bool InheritsHediffCompPostTickInterval(Type type)
+ => AccessTools.Method(type, nameof(HediffComp.CompPostTickInterval),
+ [typeof(float).MakeByRefType(), typeof(int)])?.DeclaringType
+ == typeof(HediffComp);
+
+ private static bool IsTrivialVoidMethod(MethodInfo method)
+ {
+ if (method.ReturnType != typeof(void)
+ || method.GetMethodBody() is not { ExceptionHandlingClauses.Count: 0, LocalVariables.Count: 0 } body)
+ {
+ return false;
+ }
+
+ var il = body.GetILAsByteArray();
+ if (il == null || il.Length == 0)
+ return false;
+
+ var sawRet = false;
+
+ for (var i = 0; i < il.Length; i++)
+ {
+ switch (il[i])
+ {
+ case 0x00:
+ continue;
+ case 0x2A when i == il.Length - 1 && !sawRet:
+ sawRet = true;
+ continue;
+ default:
+ return false;
+ }
+ }
+
+ return sawRet;
+ }
+
+ private static bool HasHarmonyPatches(MethodBase method)
+ {
+ var patches = Harmony.GetPatchInfo(method);
+ return patches is not null
+ && (patches.Prefixes.Count != 0
+ || patches.Postfixes.Count != 0
+ || patches.Transpilers.Count != 0
+ || patches.Finalizers.Count != 0);
+ }
+}
diff --git a/Source/PerformanceFish/Defs/RecipeDefCaching.cs b/Source/PerformanceFish/Defs/RecipeDefCaching.cs
index 868047e..7720c4a 100644
--- a/Source/PerformanceFish/Defs/RecipeDefCaching.cs
+++ b/Source/PerformanceFish/Defs/RecipeDefCaching.cs
@@ -56,8 +56,8 @@ public sealed class RecipeDefCaching : ClassWithFishPatches
{
private int _defDatabaseVersion = -2;
private int _defRecipeUsersVersion = -2;
- private RecipeDef _def;
- public List RecipeUsers;
+ private RecipeDef? _def;
+ public List RecipeUsers = [];
public void Update(RecipeDef instance, IEnumerable result)
{
@@ -81,7 +81,7 @@ public sealed class RecipeDefCaching : ClassWithFishPatches
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
=> _defDatabaseVersion != DefDatabase.defsList._version
- || _defRecipeUsersVersion != (_def.recipeUsers?._version ?? 0);
+ || _defRecipeUsersVersion != (_def?.recipeUsers?._version ?? 0);
}
}
}
@@ -141,4 +141,4 @@ public sealed class RecipeDefCaching : ClassWithFishPatches
=> throw new NotImplementedException();
}
}*/
-}
\ No newline at end of file
+}
diff --git a/Source/PerformanceFish/Defs/ThingDefPatches.cs b/Source/PerformanceFish/Defs/ThingDefPatches.cs
index ffc0c67..ac98f4d 100644
--- a/Source/PerformanceFish/Defs/ThingDefPatches.cs
+++ b/Source/PerformanceFish/Defs/ThingDefPatches.cs
@@ -21,13 +21,25 @@ public sealed class ThingDefPatches : ClassWithFishPrepatches
public sealed class GetHashCodePatch : FishClassPrepatch
{
public override string? Description { get; }
+#if V1_6
+ = "Disabled on 1.6 because Verse.Def now already caches defNameHash and overrides GetHashCode itself.";
+#else
= "Def.GetHashCode is overridden to return defName.GetHashCode, which is rather slow as that's a string. "
+ "This patch caches the hashcode in a field added through prepatcher and simply returns that instead.";
+#endif
- public override Type Type { get; } = typeof(BuildableDef);
+ public override Type Type { get; } =
+#if V1_6
+ typeof(Def);
+#else
+ typeof(BuildableDef);
+#endif
public override void FreePatch(TypeDefinition typeDefinition)
{
+#if V1_6
+ return;
+#else
var module = typeDefinition.Module;
var fishHashField = new FieldDefinition(FISH_HASH_FIELD_NAME, FieldAttributes.Private,
@@ -74,6 +86,7 @@ public sealed class ThingDefPatches : ClassWithFishPrepatches
getHashCodeBody.MaxStackSize = 2;
getHashCodeBody.Method.ImplAttributes |= MethodImplAttributes.AggressiveInlining;
getHashCodeBody.OptimizeMacros();
+#endif
}
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -295,4 +308,4 @@ public sealed class ThingDefPatches : ClassWithFishPrepatches
Flammability,
MaxHitPoints
}
-}
\ No newline at end of file
+}
diff --git a/Source/PerformanceFish/Directory.Build.props b/Source/PerformanceFish/Directory.Build.props
index cc90b52..84db8c9 100644
--- a/Source/PerformanceFish/Directory.Build.props
+++ b/Source/PerformanceFish/Directory.Build.props
@@ -29,12 +29,13 @@
-
+
+
@@ -93,4 +94,4 @@
-
\ No newline at end of file
+
diff --git a/Source/PerformanceFish/GasGridOptimization.cs b/Source/PerformanceFish/GasGridOptimization.cs
index 3b5e3bf..a69bd8c 100644
--- a/Source/PerformanceFish/GasGridOptimization.cs
+++ b/Source/PerformanceFish/GasGridOptimization.cs
@@ -41,7 +41,8 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
= "Required by the gas grid optimization";
public override MethodBase TargetMethodBase { get; }
- = AccessTools.DeclaredMethod(typeof(GasGrid), nameof(GasGrid.SetDirect));
+ = AccessTools.DeclaredMethod(typeof(GasGrid), nameof(GasGrid.SetDirect),
+ [typeof(int), typeof(byte), typeof(byte), typeof(byte), typeof(byte)])!;
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
@@ -415,7 +416,7 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int GetMapSizeX(Map map) => map.cellIndices.mapSizeX;
+ public static int GetMapSizeX(Map map) => map.Size.x;
}
public sealed class ExposeDataPatch : FishPrepatch
@@ -1075,7 +1076,7 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private IntVec3 IndexToCell(int index)
- => CellIndicesUtility.IndexToCell(index, Map.cellIndices.mapSizeX);
+ => CellIndicesUtility.IndexToCell(index, Map.Size.x);
///
/// For modders. Performance Fish doesn't automatically scribe added custom defs.
@@ -1136,4 +1137,4 @@ public sealed class GasGridOptimization : ClassWithFishPrepatches
}
// ReSharper restore UnusedMember.Local
}
-}
\ No newline at end of file
+}
diff --git a/Source/PerformanceFish/GenLocalDateCaching.cs b/Source/PerformanceFish/GenLocalDateCaching.cs
index a8ed3bc..baa3dc6 100644
--- a/Source/PerformanceFish/GenLocalDateCaching.cs
+++ b/Source/PerformanceFish/GenLocalDateCaching.cs
@@ -19,45 +19,17 @@ public sealed class GenLocalDateCaching : ClassWithFishPatches
+ "optimization, but yields accurate results instead of a placeholder value to avoid issues";
public override Delegate TargetMethodGroup { get; } = (Func)GenLocalDate.DayTick;
- public override int TranspilerMethodPriority => Priority.First;
+ public override int PrefixMethodPriority => Priority.First;
- public static CodeInstructions Transpiler(CodeInstructions codes, MethodBase method, ILGenerator generator)
- //=> Reflection.GetCodeInstructions(Replacement);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Prefix(Thing thing, ref int __result)
{
- var labelToStartOfMethod = generator.DefineLabel();
- codes.First().labels.Add(labelToStartOfMethod);
+ if (thing.TryGetMap() is not { } map)
+ return true;
- yield return FishTranspiler.Argument(method, "thing");
- yield return FishTranspiler.Field(typeof(Thing), nameof(Thing.mapIndexOrState));
- yield return FishTranspiler.Constant(0);
- yield return FishTranspiler.IfLessThan(labelToStartOfMethod);
-
- yield return FishTranspiler.Field(typeof(Current), nameof(Current.gameInt));
- yield return FishTranspiler.Field(typeof(Game), nameof(Game.maps));
- yield return FishTranspiler.Argument(method, "thing");
- yield return FishTranspiler.Field(typeof(Thing), nameof(Thing.mapIndexOrState));
- yield return FishTranspiler.Call(typeof(List