Added stat caching, fixed all the bugs
Added RitualObligationTriggers analyzer category, Fixed defs from other mods with broken market value breaking fish's storage patches, Fixed storage caches not updating for quality, rotting and similar changes to stored things, Fixed storage caches not updating capacity when reinstalling storage, Fixed prison labor compatibility, Improved multiplayer compatibility, Fixed ideology style dominance and altar sharing patches throwing errors instead of defaulting to disabled when not having ideology, Fixed compatibility with RIMMSqol's world pawn GC patch by disabling that patch, Fixed rare threading issues throwing errors on startup
This commit is contained in:
Binary file not shown.
Binary file not shown.
+79
-71
@@ -118,36 +118,6 @@
|
||||
</NestedTypes-->
|
||||
</PerformanceFish>
|
||||
|
||||
<ThingCompTick>
|
||||
<Tooltip>Tick methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>Verse.ThingComp:CompTick</li>
|
||||
<li>Verse.ThingComp:CompTickRare</li>
|
||||
<li>Verse.ThingComp:CompTickLong</li>
|
||||
</DerivedMethods>
|
||||
</ThingCompTick>
|
||||
|
||||
<ThingCompUpdate>
|
||||
<Tooltip>Update methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones</Tooltip>
|
||||
<Category>Update</Category>
|
||||
<DerivedMethods>
|
||||
<li>Verse.ThingComp:DrawGUIOverlay</li>
|
||||
<li>Verse.ThingComp:PostDraw</li>
|
||||
<li>Verse.ThingComp:PostDrawExtraSelectionOverlays</li>
|
||||
<li>Verse.ThingComp:PostPrintOnto</li>
|
||||
</DerivedMethods>
|
||||
</ThingCompUpdate>
|
||||
|
||||
<Hediffs>
|
||||
<Tooltip>Ticking health stuff ond other things implanted in pawn bodies</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>Verse.Hediff:Tick</li>
|
||||
<li>Verse.Hediff:PostTick</li>
|
||||
</DerivedMethods>
|
||||
</Hediffs>
|
||||
|
||||
<Abilities>
|
||||
<Tooltip>Ticking methods of abilities. Cooldown, effects, combat</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
@@ -157,16 +127,16 @@
|
||||
</DerivedMethods>
|
||||
</Abilities>
|
||||
|
||||
<Precepts>
|
||||
<Tooltip>Ticking methods of precepts. Ideology stuff.</Tooltip>
|
||||
<AbilityComps>
|
||||
<Tooltip>Comps attached to abilities</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li MayRequire="Ludeon.RimWorld.Ideology">RimWorld.Precept:Tick</li>
|
||||
<li>RimWorld.AbilityComp:CompTick</li>
|
||||
</DerivedMethods>
|
||||
</Precepts>
|
||||
</AbilityComps>
|
||||
|
||||
<Genes>
|
||||
<Tooltip>Ticking methods of genes. Yes, all the genes tick.</Tooltip>
|
||||
<Tooltip>Ticking methods of genes. Often empty when not having any ticking genes</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li MayRequire="Ludeon.RimWorld.Biotech">Verse.Gene:Tick</li>
|
||||
@@ -181,45 +151,14 @@
|
||||
</DerivedMethods>
|
||||
</HediffComps>
|
||||
|
||||
<AbilityComps>
|
||||
<Tooltip>Comps attached to abilities</Tooltip>
|
||||
<Hediffs>
|
||||
<Tooltip>Ticking health stuff ond other things implanted in pawn bodies</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.AbilityComp:CompTick</li>
|
||||
<li>Verse.Hediff:Tick</li>
|
||||
<li>Verse.Hediff:PostTick</li>
|
||||
</DerivedMethods>
|
||||
</AbilityComps>
|
||||
|
||||
<WorldObjects>
|
||||
<Tooltip>Settlements, caravans etc</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.Planet.WorldObject:Tick</li>
|
||||
</DerivedMethods>
|
||||
</WorldObjects>
|
||||
|
||||
<WorldObjectComps>
|
||||
<Tooltip>Comps attached to settlements, caravans etc</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.Planet.WorldObjectComp:CompTick</li>
|
||||
</DerivedMethods>
|
||||
</WorldObjectComps>
|
||||
|
||||
<WorldComponentTick>
|
||||
<Tooltip>Tick methods of comps with one instance attached to the game world</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.Planet.WorldComponent:WorldComponentTick</li>
|
||||
</DerivedMethods>
|
||||
</WorldComponentTick>
|
||||
|
||||
<WorldComponentUpdate>
|
||||
<Tooltip>Update methods of comps with one instance attached to the game world</Tooltip>
|
||||
<Category>Update</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.Planet.WorldComponent:WorldComponentUpdate</li>
|
||||
</DerivedMethods>
|
||||
</WorldComponentUpdate>
|
||||
</Hediffs>
|
||||
|
||||
<MentalStates>
|
||||
<Tooltip>Tick methods of mental states like berserk, tantrum etc</Tooltip>
|
||||
@@ -229,4 +168,73 @@
|
||||
</DerivedMethods>
|
||||
</MentalStates>
|
||||
|
||||
<Precepts>
|
||||
<Tooltip>Ticking methods of precepts. Ideology stuff.</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li MayRequire="Ludeon.RimWorld.Ideology">RimWorld.Precept:Tick</li>
|
||||
</DerivedMethods>
|
||||
</Precepts>
|
||||
|
||||
<RitualObligationTriggers>
|
||||
<Tooltip>Tick methods ideology ritual triggers. Can scale quite badly when having a lot of precepts loaded</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.RitualObligationTrigger:Tick</li>
|
||||
</DerivedMethods>
|
||||
</RitualObligationTriggers>
|
||||
|
||||
<ThingCompTick>
|
||||
<Tooltip>Tick methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>Verse.ThingComp:CompTick</li>
|
||||
<li>Verse.ThingComp:CompTickRare</li>
|
||||
<li>Verse.ThingComp:CompTickLong</li>
|
||||
</DerivedMethods>
|
||||
</ThingCompTick>
|
||||
|
||||
<WorldComponentTick>
|
||||
<Tooltip>Tick methods of comps with one instance attached to the game world</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.Planet.WorldComponent:WorldComponentTick</li>
|
||||
</DerivedMethods>
|
||||
</WorldComponentTick>
|
||||
|
||||
<WorldObjectComps>
|
||||
<Tooltip>Comps attached to settlements, caravans etc</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.Planet.WorldObjectComp:CompTick</li>
|
||||
</DerivedMethods>
|
||||
</WorldObjectComps>
|
||||
|
||||
<WorldObjects>
|
||||
<Tooltip>Settlements, caravans etc</Tooltip>
|
||||
<Category>Tick</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.Planet.WorldObject:Tick</li>
|
||||
</DerivedMethods>
|
||||
</WorldObjects>
|
||||
|
||||
<ThingCompUpdate>
|
||||
<Tooltip>Update methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones</Tooltip>
|
||||
<Category>Update</Category>
|
||||
<DerivedMethods>
|
||||
<li>Verse.ThingComp:DrawGUIOverlay</li>
|
||||
<li>Verse.ThingComp:PostDraw</li>
|
||||
<li>Verse.ThingComp:PostDrawExtraSelectionOverlays</li>
|
||||
<li>Verse.ThingComp:PostPrintOnto</li>
|
||||
</DerivedMethods>
|
||||
</ThingCompUpdate>
|
||||
|
||||
<WorldComponentUpdate>
|
||||
<Tooltip>Update methods of comps with one instance attached to the game world</Tooltip>
|
||||
<Category>Update</Category>
|
||||
<DerivedMethods>
|
||||
<li>RimWorld.Planet.WorldComponent:WorldComponentUpdate</li>
|
||||
</DerivedMethods>
|
||||
</WorldComponentUpdate>
|
||||
|
||||
</Analyzer>
|
||||
@@ -9,8 +9,8 @@ Performance Fish attempts to improve overall framerates and tick times by patchi
|
||||
A settings menu includes short descriptions for every patch, and each of them can be freely toggled in there. They get entirely unpatched in a disabled state, immediately. Nothing is stored in savefiles of specific game-sessions, ensuring that no errors get thrown when removing this mod.
|
||||
Special patches exist to add new entries and features to Dub's Performance Analyzer, including a right-click function allowing to profile overrides of functions.
|
||||
|
||||
Almost all mods, including Combat Extended, Vanilla Expanded, RocketMan and Performance Optimizer, are compatible.
|
||||
Multiplayer needs testing.
|
||||
Almost all mods, including Combat Extended, Multiplayer, Vanilla Expanded, RocketMan and Performance Optimizer, are compatible.
|
||||
|
||||
RimThreaded, RimWorld Rick, Oskar Obnoxious, No Laggy Beds and Better GC are currently marked as incompatible.
|
||||
|
||||
Licensed under [MPL-2.0](https://tldrlegal.com/license/mozilla-public-license-2.0-(mpl-2))
|
||||
@@ -25,6 +25,8 @@ public record struct ByInt<T, TResult> where T : notnull where TResult : new()
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => Key;
|
||||
|
||||
private static List<FishTable<ByInt<T, TResult>, TResult>> _allCaches = [];
|
||||
|
||||
private static object _valueInitializerLock = new();
|
||||
|
||||
@@ -92,23 +94,65 @@ public record struct ByInt<T, TResult> where T : notnull where TResult : new()
|
||||
public static ref TResult GetExistingReference(T key) => ref Get.GetReference(new(key));
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<ByInt<T, TResult>, TResult> InitializeNew() => Utility.AddNew(ValueInitializer);
|
||||
private static FishTable<ByInt<T, TResult>, TResult> InitializeNew()
|
||||
{
|
||||
var newCache = Utility.AddNew(ValueInitializer);
|
||||
|
||||
lock (_allCaches)
|
||||
_allCaches.Add(newCache);
|
||||
|
||||
return newCache;
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
lock (_allCaches)
|
||||
{
|
||||
foreach (var cache in _allCaches)
|
||||
cache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public record struct ByInt<T1, T2, TResult>
|
||||
where T1 : notnull where T2 : notnull where TResult : new()
|
||||
{
|
||||
private static FishTable<ByInt<T1, T2, TResult>, TResult> _get
|
||||
= Utility.AddNew<ByInt<T1, T2, TResult>, TResult>();
|
||||
public int First, Second;
|
||||
|
||||
private static List<FishTable<ByInt<T1, T2, TResult>, TResult>> _allCaches = [];
|
||||
|
||||
private static object _valueInitializerLock = new();
|
||||
|
||||
private static FishTable<ByInt<T1, T2, TResult>, TResult> _get = InitializeNew();
|
||||
|
||||
[ThreadStatic]
|
||||
private static FishTable<ByInt<T1, T2, TResult>, TResult>? _getThreadStatic;
|
||||
|
||||
private static Func<ByInt<T1, T2, TResult>, TResult>? _valueInitializer;
|
||||
|
||||
public static Func<ByInt<T1, T2, TResult>, TResult>? ValueInitializer
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
return _valueInitializer;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_valueInitializerLock)
|
||||
{
|
||||
value ??= static _ => Reflection.New<TResult>();
|
||||
_valueInitializer = value;
|
||||
_get.ValueInitializer = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FishTable<ByInt<T1, T2, TResult>, TResult> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _getThreadStatic ??= Utility.AddNew<ByInt<T1, T2, TResult>, TResult>();
|
||||
get => _getThreadStatic ??= InitializeNew();
|
||||
}
|
||||
|
||||
private static ref FishTable<ByInt<T1, T2, TResult>, TResult> GetCacheRef
|
||||
@@ -135,10 +179,12 @@ public record struct ByInt<T1, T2, TResult>
|
||||
public static ref TResult GetOrAddReference(in ByInt<T1, T2, TResult> key)
|
||||
=> ref Get.GetOrAddReference(ref Unsafe.AsRef(in key));
|
||||
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// public static ref TResult GetOrAddReference(long key) // different, worse, GetHashCode method
|
||||
// => ref Unsafe.As<FishTable<ByInt<T1, T2, TResult>, TResult>, FishTable<long, TResult>>(ref GetCacheRef)
|
||||
// .GetOrAddReference(key);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TResult GetOrAdd(int first, int second)
|
||||
=> Get.GetOrAdd(new() { First = first, Second = second });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TResult GetOrAdd(in ByInt<T1, T2, TResult> key) => Get.GetOrAdd(ref Unsafe.AsRef(in key));
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static ref TResult GetExistingReference(int first, int second)
|
||||
@@ -161,8 +207,6 @@ public record struct ByInt<T1, T2, TResult>
|
||||
First = first;
|
||||
Second = second;
|
||||
}
|
||||
|
||||
public int First, Second;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByInt<T1, T2, TResult> other)
|
||||
@@ -170,12 +214,34 @@ public record struct ByInt<T1, T2, TResult>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => HashCode.Combine(First, Second);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static FishTable<ByInt<T1, T2, TResult>, TResult> InitializeNew()
|
||||
{
|
||||
var newCache = Utility.AddNew(ValueInitializer);
|
||||
|
||||
lock (_allCaches)
|
||||
_allCaches.Add(newCache);
|
||||
|
||||
return newCache;
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
lock (_allCaches)
|
||||
{
|
||||
foreach (var cache in _allCaches)
|
||||
cache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public record struct ByInt<T1, T2, T3, TResult>
|
||||
where T1 : notnull where T2 : notnull where T3 : notnull where TResult : new()
|
||||
{
|
||||
public int First, Second, Third;
|
||||
|
||||
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult> _get
|
||||
= Utility.AddNew<ByInt<T1, T2, T3, TResult>, TResult>();
|
||||
|
||||
@@ -220,8 +286,6 @@ public record struct ByInt<T1, T2, T3, TResult>
|
||||
Second = FunctionPointers.IndexGetter<T2>.Default(second);
|
||||
Third = FunctionPointers.IndexGetter<T3>.Default(third);
|
||||
}
|
||||
|
||||
public int First, Second, Third;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByInt<T1, T2, T3, TResult> other)
|
||||
@@ -238,6 +302,8 @@ public record struct ByInt<T1, T2, T3, T4, TResult>
|
||||
where T1 : notnull where T2 : notnull where T3 : notnull where T4 : notnull
|
||||
where TResult : new()
|
||||
{
|
||||
public int First, Second, Third, Fourth;
|
||||
|
||||
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> _get
|
||||
= Utility.AddNew<ByInt<T1, T2, T3, T4, TResult>, TResult>();
|
||||
|
||||
@@ -287,8 +353,6 @@ public record struct ByInt<T1, T2, T3, T4, TResult>
|
||||
Fourth = FunctionPointers.IndexGetter<T4>.Default(fourth);
|
||||
}
|
||||
|
||||
public int First, Second, Third, Fourth;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByInt<T1, T2, T3, T4, TResult> other)
|
||||
=> (Unsafe.As<ByInt<T1, T2, T3, T4, TResult>, long>(ref this)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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/.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace PerformanceFish;
|
||||
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
|
||||
public record struct DefPair
|
||||
{
|
||||
private uint _data;
|
||||
|
||||
public ushort First
|
||||
{
|
||||
get => (ushort)(_data >> 16);
|
||||
set => _data = (_data & 0x00_00_FF_FF) | ((uint)value << 16);
|
||||
}
|
||||
|
||||
public ushort Second
|
||||
{
|
||||
get => (ushort)_data;
|
||||
set => _data = (_data & 0xFF_FF_00_00) | value;
|
||||
}
|
||||
|
||||
public DefPair(Def first, Def second) : this(first.shortHash, second.shortHash)
|
||||
{
|
||||
}
|
||||
|
||||
public DefPair(ushort first, ushort second) => _data = Create(first, second);
|
||||
|
||||
public bool Equals(DefPair other) => _data == other._data;
|
||||
|
||||
public override int GetHashCode() => unchecked((int)_data);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint Create(Def first, Def second) => Create(first.shortHash, second.shortHash);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint Create(ushort first, ushort second) => ((uint)first << 16) | second;
|
||||
}
|
||||
@@ -52,10 +52,10 @@ public sealed class RecipeDefCaching : ClassWithFishPatches
|
||||
private static void UpdateCache(RecipeDef __instance, IEnumerable<ThingDef> __result)
|
||||
=> RecipeUsersCache.GetExistingReference(__instance).Update(__instance, __result);
|
||||
|
||||
public record struct RecipeUsersCacheValue
|
||||
public record struct RecipeUsersCacheValue()
|
||||
{
|
||||
private int _defDatabaseVersion;
|
||||
private int _defRecipeUsersVersion;
|
||||
private int _defDatabaseVersion = -2;
|
||||
private int _defRecipeUsersVersion = -2;
|
||||
private RecipeDef _def;
|
||||
public List<ThingDef> RecipeUsers;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Cecil.Rocks;
|
||||
using PerformanceFish.Events;
|
||||
using PerformanceFish.Prepatching;
|
||||
using FieldAttributes = Mono.Cecil.FieldAttributes;
|
||||
using MethodImplAttributes = Mono.Cecil.MethodImplAttributes;
|
||||
@@ -109,4 +110,189 @@ public sealed class ThingDefPatches : ClassWithFishPrepatches
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BaseMarketValuePatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Caches the frequently accessed and never changing ThingDef.BaseMarketValue property. Also adds proper "
|
||||
+ "exception handling in case of errors in the market value calculation.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(ThingDef), nameof(ThingDef.BaseMarketValue));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float ReplacementBody(ThingDef instance)
|
||||
{
|
||||
var cacheValue = instance.BaseStatsCache()[(int)Stats.MarketValue];
|
||||
|
||||
return !float.IsNaN(cacheValue) ? cacheValue : GetUpdatedValue(instance);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static float GetUpdatedValue(ThingDef instance)
|
||||
=> Current.Game is null
|
||||
? GetValueSafely(instance)
|
||||
: instance.BaseStatsCache()[(int)Stats.MarketValue] = GetValueSafely(instance);
|
||||
|
||||
public static float GetValueSafely(ThingDef instance)
|
||||
=> ThingDefPatches.GetValueSafely(instance, StatDefOf.MarketValue);
|
||||
|
||||
public static float LogAndFixMarketValue(BuildableDef def, ThingDef? stuff, Exception exception)
|
||||
{
|
||||
Guard.IsNotNull(def);
|
||||
Log.Error($"Exception thrown while calculating {StatDefOf.MarketValue.label} for def '{
|
||||
def.defName}' from mod '{
|
||||
def.modContentPack?.Name ?? "null"}'. Attempting to fix this now so it doesn't happen again.\n{
|
||||
exception}");
|
||||
|
||||
RecipeDef? recipe;
|
||||
try
|
||||
{
|
||||
recipe = StatWorker_MarketValue.CalculableRecipe(def);
|
||||
// this is slow, but gets cached by perf optimizer. Might be worth replicating
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Exception thrown while calculating recipe for def '{def.defName}' from mod '{
|
||||
def.modContentPack?.Name ?? "null"}'.\n{ex}");
|
||||
recipe = null;
|
||||
}
|
||||
|
||||
if (recipe?.ingredients is { } ingredients)
|
||||
ingredients.RemoveAll(static ingredientCount => ingredientCount is null);
|
||||
|
||||
if (def.CostList is { } costList)
|
||||
costList.RemoveAll(static thingDefCountClass => thingDefCountClass?.thingDef is null);
|
||||
|
||||
try
|
||||
{
|
||||
return def.GetStatValueAbstract(StatDefOf.MarketValue, stuff);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Exception thrown while calculating market value for def '{def.defName}' from mod '{
|
||||
def.modContentPack?.Name ?? "null"}' again. Fix failed, F.\n{ex}");
|
||||
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
static BaseMarketValuePatch()
|
||||
=> StaticEvents.StaticConstructorOnStartupCalled
|
||||
+= static () => StatCaching.StatExceptionHandlers[StatDefOf.MarketValue] = LogAndFixMarketValue;
|
||||
}
|
||||
|
||||
public sealed class BaseMassPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Caches the frequently accessed and never changing ThingDef.BaseMass property. Also adds basic "
|
||||
+ "exception handling in case of errors in the mass calculation.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(ThingDef), nameof(ThingDef.BaseMass));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float ReplacementBody(ThingDef instance)
|
||||
{
|
||||
var cacheValue = instance.BaseStatsCache()[(int)Stats.Mass];
|
||||
|
||||
return !float.IsNaN(cacheValue) ? cacheValue : GetUpdatedValue(instance);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static float GetUpdatedValue(ThingDef instance)
|
||||
=> Current.Game is null
|
||||
? GetValueSafely(instance)
|
||||
: instance.BaseStatsCache()[(int)Stats.Mass] = GetValueSafely(instance);
|
||||
|
||||
public static float GetValueSafely(ThingDef instance)
|
||||
=> ThingDefPatches.GetValueSafely(instance, StatDefOf.Mass);
|
||||
}
|
||||
|
||||
public sealed class BaseFlammabilityPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Caches the frequently accessed and never changing ThingDef.BaseFlammability property. Also adds basic "
|
||||
+ "exception handling in case of errors in the mass calculation.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(ThingDef), nameof(ThingDef.BaseFlammability));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float ReplacementBody(ThingDef instance)
|
||||
{
|
||||
var cacheValue = instance.BaseStatsCache()[(int)Stats.Flammability];
|
||||
|
||||
return !float.IsNaN(cacheValue) ? cacheValue : GetUpdatedValue(instance);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static float GetUpdatedValue(ThingDef instance)
|
||||
=> Current.Game is null
|
||||
? GetValueSafely(instance)
|
||||
: instance.BaseStatsCache()[(int)Stats.Flammability] = GetValueSafely(instance);
|
||||
|
||||
public static float GetValueSafely(ThingDef instance)
|
||||
=> ThingDefPatches.GetValueSafely(instance, StatDefOf.Flammability);
|
||||
}
|
||||
|
||||
public sealed class BaseMaxHitPointsPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Caches the frequently accessed and never changing ThingDef.BaseMaxHitPoints property. Also adds basic "
|
||||
+ "exception handling in case of errors in the mass calculation.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredPropertyGetter(typeof(ThingDef), nameof(ThingDef.BaseMaxHitPoints));
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ReplacementBody(ThingDef instance)
|
||||
{
|
||||
var cacheValue = instance.BaseStatsCache()[(int)Stats.MaxHitPoints];
|
||||
|
||||
return Mathf.RoundToInt(!float.IsNaN(cacheValue) ? cacheValue : GetUpdatedValue(instance));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static float GetUpdatedValue(ThingDef instance)
|
||||
=> Current.Game is null
|
||||
? GetValueSafely(instance)
|
||||
: instance.BaseStatsCache()[(int)Stats.MaxHitPoints] = GetValueSafely(instance);
|
||||
|
||||
public static float GetValueSafely(ThingDef instance)
|
||||
=> ThingDefPatches.GetValueSafely(instance, StatDefOf.MaxHitPoints);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static float GetValueSafely(ThingDef instance, StatDef stat)
|
||||
{
|
||||
try
|
||||
{
|
||||
return instance.GetStatValueAbstract(stat);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatCaching.StatExceptionHandlers.GetOrAdd(stat)(instance, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Stats
|
||||
{
|
||||
MarketValue,
|
||||
Mass,
|
||||
Flammability,
|
||||
MaxHitPoints
|
||||
}
|
||||
}
|
||||
@@ -12,4 +12,8 @@ public static class StaticEvents
|
||||
add => Cache.Utility.Cleared += value;
|
||||
remove => Cache.Utility.Cleared -= value;
|
||||
}
|
||||
|
||||
public static event Action? StaticConstructorOnStartupCalled;
|
||||
|
||||
internal static void OnStaticConstructorOnStartupCalled() => StaticConstructorOnStartupCalled?.Invoke();
|
||||
}
|
||||
@@ -11,7 +11,8 @@ public sealed class ThingEvents : ClassWithFishPrepatches
|
||||
{
|
||||
public static event Action<Thing>?
|
||||
Initialized,
|
||||
Destroyed;
|
||||
Destroyed,
|
||||
QualityChanging;
|
||||
|
||||
public sealed class Instanced
|
||||
{
|
||||
@@ -163,5 +164,20 @@ public sealed class ThingEvents : ClassWithFishPrepatches
|
||||
public static void Postfix(ThingWithComps __instance) => OnThingInitialized(__instance);
|
||||
}
|
||||
|
||||
public sealed class SetQualityPatch : FishPrepatch
|
||||
{
|
||||
public override bool ShowSettings => false;
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(CompQuality), nameof(CompQuality.SetQuality));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Prefix(CompQuality __instance, QualityCategory q)
|
||||
{
|
||||
if (q != __instance.qualityInt)
|
||||
QualityChanging?.Invoke(__instance.parent);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnThingInitialized(Thing thing) => Initialized?.Invoke(thing);
|
||||
}
|
||||
@@ -98,6 +98,6 @@ public sealed class GenLocalDateCaching : ClassWithFishPatches
|
||||
}
|
||||
|
||||
public static Map? SavedMap;
|
||||
public static int SavedTick;
|
||||
public static int DayTick;
|
||||
public static int SavedTick = -2;
|
||||
public static int DayTick = -2;
|
||||
}
|
||||
@@ -51,9 +51,9 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
return null;
|
||||
}
|
||||
|
||||
public record struct CacheValue<T> where T : ThingComp
|
||||
public record struct CacheValue<T>() where T : ThingComp
|
||||
{
|
||||
private int _listVersion = -1;
|
||||
private int _listVersion = -2;
|
||||
public T? Comp;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -68,8 +68,6 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
_listVersion = thing.comps._version;
|
||||
Comp = comp;
|
||||
}
|
||||
|
||||
public CacheValue() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +111,9 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
return null;
|
||||
}
|
||||
|
||||
public record struct CacheValue<T> where T : CompProperties
|
||||
public record struct CacheValue<T>() where T : CompProperties
|
||||
{
|
||||
private int _listVersion = -1;
|
||||
private int _listVersion = -2;
|
||||
public T? Comp;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -130,8 +128,6 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
_listVersion = thingDef.comps._version;
|
||||
Comp = comp;
|
||||
}
|
||||
|
||||
public CacheValue() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,9 +172,9 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
return false;
|
||||
}
|
||||
|
||||
public record struct CacheValue
|
||||
public record struct CacheValue()
|
||||
{
|
||||
private int _listVersion = -1;
|
||||
private int _listVersion = -2;
|
||||
public bool HasComp;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -193,8 +189,6 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
_listVersion = thingDef.comps._version;
|
||||
HasComp = hasComp;
|
||||
}
|
||||
|
||||
public CacheValue() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,9 +231,9 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
return null;
|
||||
}
|
||||
|
||||
public record struct CacheValue<T> where T : HediffComp
|
||||
public record struct CacheValue<T>() where T : HediffComp
|
||||
{
|
||||
private int _listVersion = -1;
|
||||
private int _listVersion = -2;
|
||||
public T? Comp;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -254,8 +248,6 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
_listVersion = hediff.comps._version;
|
||||
Comp = comp;
|
||||
}
|
||||
|
||||
public CacheValue() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,9 +290,9 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
return null;
|
||||
}
|
||||
|
||||
public record struct CacheValue<T> where T : HediffCompProperties
|
||||
public record struct CacheValue<T>() where T : HediffCompProperties
|
||||
{
|
||||
private int _listVersion = -1;
|
||||
private int _listVersion = -2;
|
||||
public T? Comp;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -315,8 +307,6 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
_listVersion = hediffDef.comps._version;
|
||||
Comp = comp;
|
||||
}
|
||||
|
||||
public CacheValue() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,9 +349,9 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
return null;
|
||||
}
|
||||
|
||||
public record struct CacheValue<T> where T : AbilityComp
|
||||
public record struct CacheValue<T>() where T : AbilityComp
|
||||
{
|
||||
private int _listVersion = -1;
|
||||
private int _listVersion = -2;
|
||||
public T? Comp;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -376,8 +366,6 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
_listVersion = ability.comps._version;
|
||||
Comp = comp;
|
||||
}
|
||||
|
||||
public CacheValue() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,9 +403,9 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
return null;
|
||||
}
|
||||
|
||||
public record struct CacheValue<T> where T : WorldObjectComp
|
||||
public record struct CacheValue<T>() where T : WorldObjectComp
|
||||
{
|
||||
private int _listVersion = -1;
|
||||
private int _listVersion = -2;
|
||||
public T? Comp;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -432,8 +420,6 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
_listVersion = worldObject.comps._version;
|
||||
Comp = comp;
|
||||
}
|
||||
|
||||
public CacheValue() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,9 +457,9 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
return null;
|
||||
}
|
||||
|
||||
public record struct CacheValue<T> where T : MapComponent
|
||||
public record struct CacheValue<T>() where T : MapComponent
|
||||
{
|
||||
private int _listVersion = -1;
|
||||
private int _listVersion = -2;
|
||||
public T? Comp;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -488,8 +474,6 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
_listVersion = map.components._version;
|
||||
Comp = comp;
|
||||
}
|
||||
|
||||
public CacheValue() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,7 +511,7 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
|
||||
public static class Cache<T> where T : WorldComponent
|
||||
{
|
||||
private static int _listVersion = -1;
|
||||
private static int _listVersion = -2;
|
||||
private static World? _world;
|
||||
public static T? Comp;
|
||||
|
||||
@@ -578,7 +562,7 @@ public sealed class GetCompCaching : ClassWithFishPrepatches
|
||||
|
||||
public static class Cache<T> where T : GameComponent
|
||||
{
|
||||
private static int _listVersion = -1;
|
||||
private static int _listVersion = -2;
|
||||
private static Game? _game;
|
||||
public static T? Comp;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace PerformanceFish.Hauling;
|
||||
|
||||
public record struct HaulDestinationManagerCache
|
||||
public record struct HaulDestinationManagerCache()
|
||||
{
|
||||
public static readonly int StoragePriorityCount = Enum.GetValues(typeof(StoragePriority)).Length;
|
||||
|
||||
@@ -24,8 +24,4 @@ public record struct HaulDestinationManagerCache
|
||||
|
||||
PriorityChanged?.Invoke(manager);
|
||||
}
|
||||
|
||||
public HaulDestinationManagerCache()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,11 @@ public sealed class HaulDestinationManagerPatches : ClassWithFishPrepatches
|
||||
|
||||
public static void Postfix(HaulDestinationManager __instance, IHaulDestination haulDestination)
|
||||
{
|
||||
if (haulDestination is ISlotGroupParent)
|
||||
__instance.Cache().OnPriorityChanged(__instance);
|
||||
if (haulDestination is not ISlotGroupParent slotGroupParent)
|
||||
return;
|
||||
|
||||
__instance.Cache().OnPriorityChanged(__instance);
|
||||
slotGroupParent.GetSlotGroup().NotifyCellCountChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +49,11 @@ public sealed class HaulDestinationManagerPatches : ClassWithFishPrepatches
|
||||
|
||||
public static void Postfix(HaulDestinationManager __instance, IHaulDestination haulDestination)
|
||||
{
|
||||
if (haulDestination is ISlotGroupParent)
|
||||
__instance.Cache().OnPriorityChanged(__instance);
|
||||
if (haulDestination is not ISlotGroupParent slotGroupParent)
|
||||
return;
|
||||
|
||||
__instance.Cache().OnPriorityChanged(__instance);
|
||||
slotGroupParent.GetSlotGroup().ResetDistricts();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,18 +18,32 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
public override string Description { get; }
|
||||
= "Caches results of storage settings checks for things when hauling";
|
||||
|
||||
protected internal override void OnPatchingCompleted()
|
||||
=> Events.ThingEvents.QualityChanging += NotifyQualityChanging;
|
||||
|
||||
public static void NotifyQualityChanging(Thing thing)
|
||||
{
|
||||
if (!thing.IsItem() || thing.TryGetMap() is not { } map)
|
||||
return;
|
||||
|
||||
var haulDestinations = map.haulDestinationManager.AllHaulDestinationsListForReading;
|
||||
for (var i = haulDestinations.Count; i-- > 0;)
|
||||
haulDestinations[i].GetStoreSettings().Cache().AllowedToAccept.Remove(thing.thingIDNumber);
|
||||
}
|
||||
|
||||
public override Expression<Action> TargetMethod { get; }
|
||||
= static () => default(StorageSettings)!.AllowedToAccept(default(Thing));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(StorageSettings __instance, Thing t, ref bool __result, out bool __state)
|
||||
{
|
||||
var cachedAllowedToAcceptValue = __instance.Cache().AllowedToAccept.GetOrAdd(t.thingIDNumber);
|
||||
ref var allowedToAcceptCacheValue
|
||||
= ref __instance.Cache().AllowedToAccept.GetOrAddReference(t.thingIDNumber);
|
||||
|
||||
if (StorageSettingsCache.IsDirty(cachedAllowedToAcceptValue))
|
||||
if (allowedToAcceptCacheValue.Dirty)
|
||||
return __state = true;
|
||||
|
||||
__result = cachedAllowedToAcceptValue.AsBool() /*&& CapacityAllows(__instance, t)*/;
|
||||
__result = allowedToAcceptCacheValue.Value;
|
||||
|
||||
return __state = false;
|
||||
}
|
||||
@@ -45,7 +59,8 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void UpdateCache(StorageSettings __instance, Thing t, bool __result)
|
||||
=> __instance.Cache().Update(t.thingIDNumber, __result && t is { def: not null, thingIDNumber: not -1 });
|
||||
=> __instance.Cache().AllowedToAccept.GetOrAddReference(t.thingIDNumber)
|
||||
.Update(t, __result && t is { def: not null, thingIDNumber: not -1 });
|
||||
}
|
||||
|
||||
public sealed class TryNotifyChanged_Patch : FishPatch
|
||||
@@ -107,9 +122,9 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
}
|
||||
}
|
||||
|
||||
public record struct StorageSettingsCache
|
||||
public record struct StorageSettingsCache()
|
||||
{
|
||||
public FishTable<int, int> AllowedToAccept = new();
|
||||
public FishTable<int, AllowedToAcceptCacheValue> AllowedToAccept = new();
|
||||
|
||||
private Thing? _parent;
|
||||
|
||||
@@ -129,15 +144,10 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
|
||||
public List<Thing> StoredThingsOfDef(ThingDef def) => _storedThingsByDef.GetOrAdd(def.shortHash);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsDirty(int allowedToAcceptValue) => allowedToAcceptValue == int.MaxValue;
|
||||
|
||||
public void Update(int thingIDNumber, bool allowedToAccept)
|
||||
=> AllowedToAccept[thingIDNumber] = allowedToAccept.AsInt();
|
||||
|
||||
public void Initialize(Thing? slotGroupParent)
|
||||
{
|
||||
_parent = slotGroupParent;
|
||||
_storedThingCount = 0;
|
||||
_storedThingsByDef.Clear();
|
||||
AllowedToAccept.Clear();
|
||||
_lwmProps = slotGroupParent?.TryGetLwmCompProperties();
|
||||
@@ -177,8 +187,27 @@ public sealed class StorageSettingsPatches : ClassWithFishPatches
|
||||
|
||||
SettingsChanged?.Invoke(storageSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public StorageSettingsCache() => AllowedToAccept.ValueInitializer = static _ => int.MaxValue;
|
||||
public record struct AllowedToAcceptCacheValue()
|
||||
{
|
||||
private int
|
||||
_value = int.MaxValue,
|
||||
_nextUpdateTick = -2;
|
||||
|
||||
public bool Value => _value.AsBool();
|
||||
|
||||
public bool Dirty
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (_value == int.MaxValue) | TickHelper.Past(_nextUpdateTick);
|
||||
}
|
||||
|
||||
public void Update(Thing thing, bool allowedToAccept)
|
||||
{
|
||||
_value = allowedToAccept.AsInt();
|
||||
_nextUpdateTick = TickHelper.Add(512, thing.thingIDNumber, 128);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Debug
|
||||
|
||||
@@ -113,11 +113,7 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
var thingEvents = thing.Events();
|
||||
|
||||
if (thing is ISlotGroupParent)
|
||||
{
|
||||
thingEvents.Spawned += NotifySlotGroupCellCountChanged;
|
||||
thingEvents.Spawned += StorageSettingsPatches.InitializeSlotGroupParent;
|
||||
thingEvents.DeSpawned += NotifySlotGroupCellCountChanged;
|
||||
}
|
||||
|
||||
if (!thing.IsItem())
|
||||
return;
|
||||
@@ -126,12 +122,6 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
thingEvents.DeregisteredAtThingGrid += TryNotifyLostThing;
|
||||
}
|
||||
|
||||
public static void NotifySlotGroupCellCountChanged(Thing slotGroupParent, Map map)
|
||||
=> NotifySlotGroupCellCountChanged(slotGroupParent);
|
||||
|
||||
public static void NotifySlotGroupCellCountChanged(Thing slotGroupParent)
|
||||
=> slotGroupParent.GetSlotGroup()?.NotifyCellCountChanged();
|
||||
|
||||
public static void TryNotifyReceivedThing(Thing thing, Map map, in IntVec3 cell)
|
||||
{
|
||||
if (cell.GetSlotGroupParent(map) is { } parent and Thing)
|
||||
@@ -370,13 +360,27 @@ public sealed class StoreUtilityPrepatches : ClassWithFishPrepatches
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void TryRemoveFromListerHaulables(Thing t, StoragePriority currentPriority)
|
||||
{
|
||||
if (ModCompatibility.ActiveMods.Multiplayer | !HaulablesTickPatchActive)
|
||||
return;
|
||||
|
||||
if (t.IsInAnyStorage() /*t.IsInValidStorage()*/ && t.TryGetMapHeld() is { } map)
|
||||
map.listerHaulables.TryRemoveDirectly(t);
|
||||
|
||||
map.listerHaulables.Cache().ThingsQueuedToRemove.Add(t);
|
||||
|
||||
// remove for any storage instead of only valid storage to prevent further haul attempts until the next
|
||||
// automatic ListerHaulablesTick cycle
|
||||
}
|
||||
|
||||
private static int _haulablesTickPatchActive = int.MaxValue;
|
||||
|
||||
private static bool HaulablesTickPatchActive
|
||||
=> _haulablesTickPatchActive != int.MaxValue
|
||||
? _haulablesTickPatchActive.AsBool()
|
||||
: UpdateHaulablesTickPatchActive();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static bool UpdateHaulablesTickPatchActive()
|
||||
=> (_haulablesTickPatchActive = Get<Haulables.TickPatch>().IsActive.AsInt()).AsBool();
|
||||
|
||||
public static class Debug
|
||||
{
|
||||
[Conditional("STORAGE_GROUP_DEBUG")]
|
||||
|
||||
@@ -123,10 +123,10 @@ public sealed class HediffSetPrecaching : ClassWithFishPrepatches
|
||||
public static bool Prefix(HediffSet __instance, BodyPartHeight height, BodyPartDepth depth, BodyPartTagDef? tag,
|
||||
BodyPartRecord? partParent, ref IEnumerable<BodyPartRecord> __result, out bool __state)
|
||||
{
|
||||
if (height != BodyPartHeight.Undefined
|
||||
|| depth != BodyPartDepth.Undefined
|
||||
|| tag != null
|
||||
|| partParent != null)
|
||||
if ((height != BodyPartHeight.Undefined)
|
||||
| (depth != BodyPartDepth.Undefined)
|
||||
| (tag != null)
|
||||
| (partParent != null))
|
||||
{
|
||||
__state = false;
|
||||
return true;
|
||||
@@ -173,7 +173,7 @@ public sealed class HediffSetCaching : ClassWithFishPatches
|
||||
}
|
||||
}
|
||||
|
||||
public record struct NotMissingPartsCacheValue
|
||||
public record struct NotMissingPartsCacheValue()
|
||||
{
|
||||
private int _version = -1;
|
||||
private List<Hediff> _hediffsInSet = [];
|
||||
@@ -200,13 +200,9 @@ public sealed class HediffSetCaching : ClassWithFishPatches
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _version != _hediffsInSet._version || TickHelper.Past(_nextRefreshTick);
|
||||
}
|
||||
|
||||
public NotMissingPartsCacheValue()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public record struct HediffCacheValue
|
||||
public record struct HediffCacheValue()
|
||||
{
|
||||
private int _version = -1;
|
||||
private List<Hediff> _hediffsInSet = [];
|
||||
@@ -227,21 +223,13 @@ public sealed class HediffSetCaching : ClassWithFishPatches
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _version != _hediffsInSet._version || TickHelper.Past(_nextRefreshTick);
|
||||
}
|
||||
|
||||
public HediffCacheValue()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public record struct VisibleHediffCacheValue
|
||||
public record struct VisibleHediffCacheValue()
|
||||
{
|
||||
public HediffCacheValue Value = new();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Update(HediffSet set, Hediff? hediff) => Value.Update(set, hediff);
|
||||
|
||||
public VisibleHediffCacheValue()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,19 +56,10 @@ public sealed class ImmunityHandlerCaching : ClassWithFishPatches
|
||||
|
||||
public sealed class ImmunityRecordExists : FishPatch
|
||||
{
|
||||
public override List<Type> LinkedPatches { get; } = [typeof(TryAddImmunityRecord)];
|
||||
|
||||
public override string? Description { get; } = "Required by the TryAddImmunityRecord optimization.";
|
||||
|
||||
public override bool Enabled
|
||||
{
|
||||
set
|
||||
{
|
||||
if (base.Enabled == value)
|
||||
return;
|
||||
|
||||
Get<TryAddImmunityRecord>().Enabled = base.Enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.Method(typeof(ImmunityHandler), nameof(ImmunityHandler.ImmunityRecordExists));
|
||||
|
||||
@@ -84,19 +75,10 @@ public sealed class ImmunityHandlerCaching : ClassWithFishPatches
|
||||
|
||||
public sealed class TryAddImmunityRecord : FishPatch
|
||||
{
|
||||
public override List<Type> LinkedPatches { get; } = [typeof(ImmunityRecordExists)];
|
||||
|
||||
public override string? Description { get; }
|
||||
= "Optimization for this specific method to not run more checks than necessary.";
|
||||
|
||||
public override bool Enabled
|
||||
{
|
||||
set
|
||||
{
|
||||
if (base.Enabled == value)
|
||||
return;
|
||||
|
||||
Get<ImmunityRecordExists>().Enabled = base.Enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.Method(typeof(ImmunityHandler), nameof(ImmunityHandler.TryAddImmunityRecord));
|
||||
@@ -110,7 +92,7 @@ public sealed class ImmunityHandlerCaching : ClassWithFishPatches
|
||||
}
|
||||
}
|
||||
|
||||
public record struct ImmunityInfoCacheValue
|
||||
public record struct ImmunityInfoCacheValue()
|
||||
{
|
||||
private int _version = -1;
|
||||
private List<Hediff> _hediffsInSet = [];
|
||||
@@ -132,15 +114,11 @@ public sealed class ImmunityHandlerCaching : ClassWithFishPatches
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _version != _hediffsInSet._version || TickHelper.Past(_nextRefreshTick);
|
||||
}
|
||||
|
||||
public ImmunityInfoCacheValue()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public record struct ImmunityRecordExistsCacheValue
|
||||
public record struct ImmunityRecordExistsCacheValue()
|
||||
{
|
||||
public bool Value;
|
||||
public int ImmunityListVersion;
|
||||
public int ImmunityListVersion = -2;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ public sealed class IdeoUtilityCaching : ClassWithFishPrepatches
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(Thing? t, Ideo? ideo, ref float __result, out bool __state)
|
||||
{
|
||||
if (t is null || ideo is null || t.thingIDNumber < 0)
|
||||
if (t is null || t.thingIDNumber < 0 || ideo is null)
|
||||
{
|
||||
__state = false;
|
||||
return true;
|
||||
@@ -51,10 +51,10 @@ public sealed class IdeoUtilityCaching : ClassWithFishPrepatches
|
||||
=> StyleDominanceCache.GetExistingReference(t, ideo).Update(__result, t);
|
||||
}
|
||||
|
||||
public record struct StyleDominanceCacheValue
|
||||
public record struct StyleDominanceCacheValue()
|
||||
{
|
||||
public float Value;
|
||||
private int _nextRefreshTick;
|
||||
private int _nextRefreshTick = -2;
|
||||
|
||||
public void Update(float value, Thing thing)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ using IngredientCacheList
|
||||
bool Found)>;
|
||||
using OpCodes = System.Reflection.Emit.OpCodes;
|
||||
using RecipeIngredientCache
|
||||
= PerformanceFish.Cache.ByIndex<RimWorld.Bill,
|
||||
= PerformanceFish.Cache.ByInt<RimWorld.Bill,
|
||||
PerformanceFish.JobSystem.WorkGiver_DoBillOptimization.RecipeIngredientCacheValue>;
|
||||
|
||||
namespace PerformanceFish.JobSystem;
|
||||
@@ -96,7 +96,7 @@ public sealed class WorkGiver_DoBillPrepatches : ClassWithFishPrepatches
|
||||
}
|
||||
else
|
||||
{
|
||||
ref var cache = ref RecipeIngredientCache.Get.GetReference(bill);
|
||||
ref var cache = ref RecipeIngredientCache.GetOrAddReference(bill.GetLoadID());
|
||||
if (cache.thingDefs is null || cache.Dirty)
|
||||
{
|
||||
cache.thingDefs = UpdateCache(bill, r, cache.thingDefs);
|
||||
@@ -501,7 +501,7 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
|
||||
if (thingValidatorInstance is null)
|
||||
return;
|
||||
|
||||
var cache = RecipeIngredientCache.Get[thingValidatorInstance.Bill].thingDefs;
|
||||
var cache = RecipeIngredientCache.GetOrAdd(thingValidatorInstance.Bill.loadID).thingDefs;
|
||||
if (cache is null)
|
||||
return;
|
||||
|
||||
@@ -583,7 +583,7 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
|
||||
return;
|
||||
}
|
||||
|
||||
var cache = RecipeIngredientCache.Get[bill].thingDefs;
|
||||
var cache = RecipeIngredientCache.GetOrAdd(bill.loadID).thingDefs;
|
||||
if (cache is null)
|
||||
return;
|
||||
|
||||
@@ -599,10 +599,10 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
|
||||
}
|
||||
}
|
||||
|
||||
public record struct RecipeIngredientCacheValue
|
||||
public record struct RecipeIngredientCacheValue()
|
||||
{
|
||||
public IngredientCacheList? thingDefs;
|
||||
private int _nextRefreshTick;
|
||||
private int _nextRefreshTick = -2;
|
||||
|
||||
public void Update(Bill bill)
|
||||
=> _nextRefreshTick = TickHelper.Add(GenTicks.TickRareInterval * 2, bill.loadID);
|
||||
|
||||
@@ -225,7 +225,7 @@ public sealed class Buildings : ClassWithFishPrepatches
|
||||
}
|
||||
}
|
||||
|
||||
public readonly record struct Cache
|
||||
public readonly record struct Cache()
|
||||
{
|
||||
public readonly FishTable<ThingDef, IndexedFishSet<Building>>
|
||||
ColonistBuildingsByDef = new(),
|
||||
@@ -240,9 +240,5 @@ public sealed class Buildings : ClassWithFishPrepatches
|
||||
ValueInitializer = static type
|
||||
=> (IList)Activator.CreateInstance(typeof(IndexedFishSet<>).MakeGenericType(type))
|
||||
};
|
||||
|
||||
public Cache()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using MonoMod.Utils;
|
||||
using PerformanceFish.ModCompatibility;
|
||||
using PerformanceFish.Prepatching;
|
||||
|
||||
namespace PerformanceFish.Listers;
|
||||
@@ -14,6 +15,8 @@ public sealed class Haulables : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class CheckPatch : FishPrepatch
|
||||
{
|
||||
public override List<string> IncompatibleModIDs { get; } = [PackageIDs.MULTIPLAYER];
|
||||
|
||||
public override string? Description { get; }
|
||||
= "Optimizes the ListerHaulables by making frequent checks that happen every tick to only check for adding "
|
||||
+ "new haulables, skipping things that are already inside the lister. This generally results in them "
|
||||
@@ -95,7 +98,8 @@ public sealed class Haulables : ClassWithFishPrepatches
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Essentially fixes a bug that was causing the method to tick cells multiple times for storages with less "
|
||||
+ "than 4 cells. Not ticking multiple times leads to a performance improvement";
|
||||
+ "than 4 cells. Not ticking multiple times leads to a performance improvement. Also handles things queued "
|
||||
+ "for removal by the StoreUtilityPrepatches:TryFindBestBetterStoreCellFor patch";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(ListerHaulables), nameof(ListerHaulables.ListerHaulablesTick));
|
||||
@@ -116,12 +120,33 @@ public sealed class Haulables : ClassWithFishPrepatches
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetCellCountToTick(int original, SlotGroup slotGroup)
|
||||
=> Math.Min(original, slotGroup.CellsList.Count);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Postfix(ListerHaulables __instance)
|
||||
{
|
||||
var thingsQueuedToRemove = __instance.Cache().ThingsQueuedToRemove;
|
||||
if (thingsQueuedToRemove.Count == 0)
|
||||
return;
|
||||
|
||||
RemoveQueuedThings(__instance, thingsQueuedToRemove);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void RemoveQueuedThings(ListerHaulables __instance, HashSet<Thing> thingsQueuedToRemove)
|
||||
{
|
||||
foreach (var thing in thingsQueuedToRemove)
|
||||
__instance.TryRemoveDirectly(thing);
|
||||
|
||||
thingsQueuedToRemove.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public record struct Cache
|
||||
public record struct Cache()
|
||||
{
|
||||
public readonly FishSet<int> ContainedThings = [];
|
||||
|
||||
public readonly HashSet<Thing> ThingsQueuedToRemove = [];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CheckUpdate(List<Thing> haulables)
|
||||
{
|
||||
@@ -140,10 +165,6 @@ public sealed class Haulables : ClassWithFishPrepatches
|
||||
for (var i = haulables.Count; i-- > 0;)
|
||||
ContainedThings.Add(haulables[i].GetKey());
|
||||
}
|
||||
|
||||
public Cache()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,11 +71,11 @@ public sealed class MassUtilityCaching : ClassWithFishPatches
|
||||
=> InventoryMassCache.GetExistingReference(p).Update(__result, p);
|
||||
}
|
||||
|
||||
public record struct GearMassCacheValue
|
||||
public record struct GearMassCacheValue()
|
||||
{
|
||||
public float Mass;
|
||||
private int _nextRefreshTick;
|
||||
private int _equipmentListsState;
|
||||
private int _nextRefreshTick = -2;
|
||||
private int _equipmentListsState = -69123;
|
||||
|
||||
public void Update(float mass, Pawn p)
|
||||
{
|
||||
@@ -87,9 +87,13 @@ public sealed class MassUtilityCaching : ClassWithFishPatches
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetEquipmentListsState(Pawn p)
|
||||
{
|
||||
var result = p.apparel != null ? p.apparel.WornApparel._version : 0;
|
||||
var result = p.apparel != null ? p.apparel.WornApparel._version : -1;
|
||||
if (p.equipment != null)
|
||||
result += p.equipment.AllEquipmentListForReading._version;
|
||||
{
|
||||
var equipmentListVersion = (uint)p.equipment.AllEquipmentListForReading._version;
|
||||
result += (int)((equipmentListVersion << 16) | (equipmentListVersion >> 16));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -99,24 +103,23 @@ public sealed class MassUtilityCaching : ClassWithFishPatches
|
||||
|| _equipmentListsState != GetEquipmentListsState(p);
|
||||
}
|
||||
|
||||
public record struct InventoryMassCacheValue
|
||||
public record struct InventoryMassCacheValue()
|
||||
{
|
||||
public float Mass;
|
||||
private int _nextRefreshTick;
|
||||
private int _inventoryListsState;
|
||||
private int _nextRefreshTick = -2;
|
||||
private int _inventoryListsState = -2;
|
||||
|
||||
public void Update(float mass, Pawn p)
|
||||
{
|
||||
Mass = mass;
|
||||
//shorter refresh interval because of stack sizes not being considered by the listVersion check
|
||||
_nextRefreshTick = TickHelper.Add(GenTicks.TickRareInterval, p.thingIDNumber);
|
||||
_inventoryListsState
|
||||
= GetInventoryListsState(
|
||||
p); //shorter refresh interval because of stack sizes not being considered by the listVersion check
|
||||
_inventoryListsState = GetInventoryListsState(p);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetInventoryListsState(Pawn p)
|
||||
=> p.inventory?.innerContainer is { } container ? container.innerList._version : 0;
|
||||
=> p.inventory?.innerContainer is { } container ? container.innerList._version : -42;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsDirty(Pawn p)
|
||||
|
||||
@@ -46,9 +46,9 @@ public sealed class MeditationUtilityCaching : ClassWithFishPatches
|
||||
.Update(__result, pawn);
|
||||
}
|
||||
|
||||
public record struct PsyfocusCacheValue
|
||||
public record struct PsyfocusCacheValue()
|
||||
{
|
||||
private int _nextRefreshTick;
|
||||
private int _nextRefreshTick = -2;
|
||||
public float PsyfocusGain;
|
||||
|
||||
public void Update(float psyfocusGain, Pawn pawn)
|
||||
|
||||
@@ -16,6 +16,8 @@ public static class ActiveMods
|
||||
Multiplayer = Contains(PackageIDs.MULTIPLAYER),
|
||||
PerformanceOptimizer = Contains(PackageIDs.PERFORMANCE_OPTIMIZER),
|
||||
Prepatcher = Contains(PackageIDs.PREPATCHER),
|
||||
PrisonLabor = Contains(PackageIDs.PRISON_LABOR),
|
||||
RIMMSqol = Contains(PackageIDs.RIMMSQOL),
|
||||
UseBestMaterials = Contains(PackageIDs.USE_BEST_MATERIALS),
|
||||
VanillaExpandedFramework = Contains(PackageIDs.VANILLA_EXPANDED_FRAMEWORK);
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ public static class PackageIDs
|
||||
PERFORMANCE_FISH = "bs.performance",
|
||||
PERFORMANCE_OPTIMIZER = "Taranchuk.PerformanceOptimizer",
|
||||
PREPATCHER = "zetrith.prepatcher",
|
||||
PRISON_LABOR = "avius.prisonlabor",
|
||||
RIMMSQOL = "MalteSchulze.RIMMSqol",
|
||||
USE_BEST_MATERIALS = "kathanon.UseBestMaterials",
|
||||
VANILLA_EXPANDED_FRAMEWORK = "OskarPotocki.VanillaFactionsExpanded.Core";
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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.ModCompatibility;
|
||||
|
||||
public static class RIMMSqol
|
||||
{
|
||||
private const string ASSEMBLY_NAME = "RIMMSqol";
|
||||
|
||||
public static bool Active => ActiveMods.RIMMSqol;
|
||||
|
||||
public static string[] BlockedPatchNamespaces { get; } = ["WorldPawnGC"];
|
||||
|
||||
public static ModContentPack? ModContentPack { get; }
|
||||
= ActiveMods.TryGetModContentPack(PackageIDs.RIMMSQOL);
|
||||
|
||||
public static Type? HarmonyPatchNamespaceType { get; }
|
||||
|
||||
public static MethodInfo? RegisterAllMethod { get; }
|
||||
|
||||
static RIMMSqol()
|
||||
{
|
||||
if (ModContentPack is null)
|
||||
return;
|
||||
|
||||
if (ModContentPack.TryGetAssembly(ASSEMBLY_NAME) is not { } assembly)
|
||||
{
|
||||
LogFailureToFind("assembly");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((HarmonyPatchNamespaceType = assembly.GetType("RIMMSqol.HarmonyPatchNamespace")) is null)
|
||||
{
|
||||
LogFailureToFind("harmony patch namespace type");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((RegisterAllMethod = AccessTools.DeclaredMethod(HarmonyPatchNamespaceType, "registerAll")) is null)
|
||||
LogFailureToFind("registerAll method");
|
||||
}
|
||||
|
||||
private static void LogFailureToFind(string text)
|
||||
=> Log.Error($"Performance Fish failed to find RIMMSqol's {
|
||||
text}. This is likely to cause issues.");
|
||||
|
||||
public static bool TryPatch()
|
||||
=> RegisterAllMethod is { } registerAllMethod
|
||||
&& PerformanceFishMod.Harmony.Patch(registerAllMethod, postfix: new(methodof(RegisterAllPostfix))) != null;
|
||||
|
||||
public static void RegisterAllPostfix()
|
||||
{
|
||||
if (AccessTools.Field(HarmonyPatchNamespaceType, "namespaces").GetValue(null) is not IDictionary namespaces)
|
||||
{
|
||||
LogFailureToFind("harmony patch namespaces field");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var blockedPatchNamespace in BlockedPatchNamespaces)
|
||||
{
|
||||
if (!namespaces.Contains(blockedPatchNamespace))
|
||||
{
|
||||
LogFailureToFind(blockedPatchNamespace);
|
||||
continue;
|
||||
}
|
||||
|
||||
var properties = namespaces[blockedPatchNamespace];
|
||||
if (AccessTools.Field(properties.GetType(), "active") is not { } activeField)
|
||||
{
|
||||
LogFailureToFind("harmony patch active field");
|
||||
return;
|
||||
}
|
||||
|
||||
activeField.SetValue(properties, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public static class OnAssembliesLoaded
|
||||
|
||||
TryPatchGraphicsSetter();
|
||||
TryPatchPerformanceOptimizer();
|
||||
TryPatchRIMMSqol();
|
||||
|
||||
TryInitialize(EqualityComparerOptimization.Optimization.Initialize);
|
||||
|
||||
@@ -72,6 +73,31 @@ public static class OnAssembliesLoaded
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
private static void TryPatchRIMMSqol()
|
||||
{
|
||||
if (!ActiveMods.RIMMSqol)
|
||||
return;
|
||||
|
||||
var success = false;
|
||||
Exception? exception = null;
|
||||
|
||||
try
|
||||
{
|
||||
success = RIMMSqol.TryPatch();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Log.Error($"Failed to apply compatibility patch for {
|
||||
RIMMSqol.ModContentPack?.Name ?? "RIMMSqol"}.".AppendNewLineWhenNotNull(exception));
|
||||
}
|
||||
}
|
||||
|
||||
private static async void TryInitialize(Action action, bool async = false)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using PerformanceFish.Events;
|
||||
|
||||
namespace PerformanceFish;
|
||||
|
||||
@@ -20,6 +21,7 @@ public static class OnStartup
|
||||
// => !MothballOptimization.AllowedDefs.Contains(def)).ToStringSafeEnumerable()}");
|
||||
|
||||
Fields.Initialized = true;
|
||||
StaticEvents.OnStaticConstructorOnStartupCalled();
|
||||
}
|
||||
|
||||
public static class State
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<Configurations>v1.4</Configurations>
|
||||
<Version>0.3.999.92</Version>
|
||||
<Version>0.5.0.0</Version>
|
||||
<NoWarn>
|
||||
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
|
||||
@@ -50,7 +50,7 @@
|
||||
</When -->
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Lib.Harmony" Version="2.*" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Lib.Harmony" Version="2.2.*" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Zetrith.Prepatcher" Version="1.*" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" ExcludeAssets="runtime" /><!-- Version included in Unity is missing IsNullRef -->
|
||||
|
||||
@@ -48,7 +48,7 @@ public sealed class PerformanceFishMod : Mod
|
||||
PACKAGE_ID = PackageIDs.PERFORMANCE_FISH;
|
||||
|
||||
[Obsolete("I am not incrementing this version number")]
|
||||
public const decimal VERSION = 0.4M;
|
||||
public const decimal VERSION = 0.5M;
|
||||
|
||||
public static Version LoadedVersion => typeof(PerformanceFishMod).Assembly.GetLoadedVersion();
|
||||
|
||||
|
||||
@@ -96,16 +96,12 @@ public sealed class WorldPawnsOptimization : ClassWithFishPrepatches
|
||||
|| allPawnsDead._version != cache.AllPawnsDeadVersion;
|
||||
}
|
||||
|
||||
public record struct Cache
|
||||
public record struct Cache()
|
||||
{
|
||||
public int
|
||||
PawnsAliveVersion = -1,
|
||||
PawnsMothballedVersion = -1,
|
||||
AllPawnsAliveVersion = -1,
|
||||
AllPawnsDeadVersion = -1;
|
||||
|
||||
public Cache()
|
||||
{
|
||||
}
|
||||
PawnsAliveVersion = -2,
|
||||
PawnsMothballedVersion = -2,
|
||||
AllPawnsAliveVersion = -2,
|
||||
AllPawnsDeadVersion = -2;
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,16 @@ public static class PrepatcherFields
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref HaulDestinationManagerCache Cache(this HaulDestinationManager manager);
|
||||
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(CreateBaseStatsCache))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern float[] BaseStatsCache(this ThingDef thingDef);
|
||||
|
||||
[PrepatcherField]
|
||||
[ValueInitializer(nameof(GetEmptyArray))]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref float[] StatsCache(this BuildableDef buildableDef);
|
||||
|
||||
[PrepatcherField]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static extern ref object MonitorObject(this GasGrid gasGrid);
|
||||
@@ -155,6 +165,14 @@ public static class PrepatcherFields
|
||||
public static Listers.Buildings.Cache CreateListerBuildingsCache() => new();
|
||||
public static Listers.Haulables.Cache CreateListerHaulablesCache() => new();
|
||||
public static HaulDestinationManagerCache CreateHaulDestinationManagerCache() => new();
|
||||
public static float[] GetEmptyArray() => Array.Empty<float>();
|
||||
|
||||
public static float[] CreateBaseStatsCache()
|
||||
{
|
||||
var array = new float[Enum.GetNames(typeof(Defs.ThingDefPatches.Stats)).Length];
|
||||
StatCaching.ResetToDefaults(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static object CreateGroupMonitorObject()
|
||||
=> ParallelNoAlloc.RegisterBackgroundWaitingWorkers(Array.Empty<Action>());
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using RoomOwnersCache
|
||||
= PerformanceFish.Cache.ByIndex<Verse.Room, PerformanceFish.RoomOptimizations.Owners_Patch.CacheValue>;
|
||||
= PerformanceFish.Cache.ByInt<Verse.Room, PerformanceFish.RoomOptimizations.Owners_Patch.CacheValue>;
|
||||
using RoomRoleCache
|
||||
= PerformanceFish.Cache.ByIndex<Verse.Room, PerformanceFish.RoomOptimizations.Role_Patch.CacheValue>;
|
||||
= PerformanceFish.Cache.ByInt<Verse.Room, PerformanceFish.RoomOptimizations.Role_Patch.CacheValue>;
|
||||
using ContainedAndAdjacentThingsCache
|
||||
= PerformanceFish.Cache.ByIndex<Verse.Room,
|
||||
= PerformanceFish.Cache.ByInt<Verse.Room,
|
||||
PerformanceFish.RoomOptimizations.ContainedAndAdjacentThings_Patch.CacheValue>;
|
||||
using OpCodes = System.Reflection.Emit.OpCodes;
|
||||
|
||||
@@ -58,7 +58,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
public static bool ShouldUpdate(Room __instance)
|
||||
{
|
||||
if (__instance.role is not null
|
||||
|| !RoomRoleCache.Get[__instance.ID].Dirty)
|
||||
|| !RoomRoleCache.GetOrAddReference(__instance.ID).Dirty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -69,11 +69,11 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
}
|
||||
|
||||
private static void UpdateCache(Room __instance)
|
||||
=> RoomRoleCache.Get.GetReference(__instance).SetDirty(__instance);
|
||||
=> RoomRoleCache.GetOrAddReference(__instance.ID).SetDirty(__instance);
|
||||
|
||||
public record struct CacheValue
|
||||
public record struct CacheValue()
|
||||
{
|
||||
private int _nextUpdateTick;
|
||||
private int _nextUpdateTick = -2;
|
||||
public bool Dirty
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -97,7 +97,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(Room __instance, ref IEnumerable<Pawn> __result, out bool __state)
|
||||
{
|
||||
ref var cache = ref RoomOwnersCache.Get.GetReference(__instance.ID);
|
||||
ref var cache = ref RoomOwnersCache.GetOrAddReference(__instance.ID);
|
||||
|
||||
if (cache.Dirty)
|
||||
{
|
||||
@@ -127,36 +127,24 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void UpdateCache(Room __instance, ref IEnumerable<Pawn> __result)
|
||||
{
|
||||
ref var cache = ref RoomOwnersCache.Get.GetReference(__instance);
|
||||
ref var cache = ref RoomOwnersCache.GetOrAddReference(__instance.ID);
|
||||
|
||||
cache.Update(__instance, __result);
|
||||
|
||||
__result = cache.Owners;
|
||||
}
|
||||
|
||||
public record struct CacheValue
|
||||
public record struct CacheValue()
|
||||
{
|
||||
private int _nextRefreshTick;
|
||||
public List<Pawn> Owners;
|
||||
private int _nextRefreshTick = -2;
|
||||
public readonly List<Pawn> Owners = [];
|
||||
|
||||
public void Update(Room room, IEnumerable<Pawn>? result)
|
||||
{
|
||||
if (result is null)
|
||||
{
|
||||
(Owners ??= []).Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Owners != null)
|
||||
{
|
||||
Owners.Clear();
|
||||
Owners.AddRange(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
Owners = [..result];
|
||||
}
|
||||
}
|
||||
Owners.Clear();
|
||||
|
||||
if (result != null)
|
||||
Owners.AddRange(result);
|
||||
|
||||
_nextRefreshTick = TickHelper.Add(384, room.ID, 256);
|
||||
}
|
||||
@@ -243,11 +231,9 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
|
||||
var regions = instance.Regions;
|
||||
|
||||
ref var cache = ref ContainedAndAdjacentThingsCache.Get.GetReference(instance.ID);
|
||||
ref var cache = ref ContainedAndAdjacentThingsCache.GetOrAddReference(instance.ID);
|
||||
|
||||
if (cache.ListVersions != null
|
||||
&& cache.ListVersions.Count == regions.Count
|
||||
&& !cache.Dirty)
|
||||
if (cache.ListVersions.Count == regions.Count && !cache.Dirty)
|
||||
{
|
||||
var cacheIsAccurate = true;
|
||||
for (var i = 0; i < regions.Count; i++)
|
||||
@@ -280,7 +266,7 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
var uniqueContainedThings = instance.uniqueContainedThings;
|
||||
var uniqueContainedThingsSet = instance.uniqueContainedThingsSet;
|
||||
uniqueContainedThingsSet.Clear();
|
||||
(cache.ListVersions ??= []).Clear();
|
||||
cache.ListVersions.Clear();
|
||||
|
||||
for (var i = 0; i < regions.Count; i++)
|
||||
{
|
||||
@@ -360,11 +346,11 @@ public sealed class RoomOptimizations : ClassWithFishPatches, IHasDescription
|
||||
return uniqueContainedThings;
|
||||
}*/
|
||||
|
||||
public record struct CacheValue
|
||||
public record struct CacheValue()
|
||||
{
|
||||
private int _nextRefreshTick;
|
||||
public List<int>? ListVersions;
|
||||
public List<Thing> Things;
|
||||
private int _nextRefreshTick = -2;
|
||||
public List<int> ListVersions = [];
|
||||
public List<Thing> Things = [];
|
||||
|
||||
public void SetDirty(bool value, Room room)
|
||||
=> _nextRefreshTick = value ? 0 : TickHelper.Add(3072, room.ID, 2048);
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
// 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/.
|
||||
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using PerformanceFish.Prepatching;
|
||||
using StatsCache = PerformanceFish.Cache.ByInt<uint, ushort, PerformanceFish.StatCaching.CachedStatValue>;
|
||||
|
||||
namespace PerformanceFish;
|
||||
|
||||
public sealed class StatCaching : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class SetStatBaseValuePatch : FishPrepatch
|
||||
{
|
||||
public override bool ShowSettings => false;
|
||||
|
||||
public override MethodBase TargetMethodBase { get; } = methodof(StatExtension.SetStatBaseValue);
|
||||
|
||||
public static void Postfix(BuildableDef def, StatDef stat)
|
||||
{
|
||||
if (def is ThingDef thingDef)
|
||||
ResetToDefaults(thingDef.BaseStatsCache());
|
||||
|
||||
ResetToDefaults(def.StatsCache());
|
||||
StatsCache.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class GetStatValueAbstractPatch : FishPrepatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Caching for stats calculated from defs, which can be expected to not change during regular gameplay. "
|
||||
+ "Also adds basic exception handling in case of errors in their calculation.";
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= methodof((Func<BuildableDef, StatDef, ThingDef, float>)StatExtension.GetStatValueAbstract);
|
||||
|
||||
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
|
||||
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
|
||||
|
||||
public static float ReplacementBody(BuildableDef def, StatDef stat, ThingDef? stuff = null)
|
||||
{
|
||||
var statsCache = def.StatsCache();
|
||||
if (statsCache.Length > stat.index)
|
||||
{
|
||||
var cacheValue = stuff is null ? statsCache[stat.index] : GetStuffed(def, stat, stuff);
|
||||
|
||||
if (!float.IsNaN(cacheValue))
|
||||
return cacheValue;
|
||||
}
|
||||
|
||||
return TryGetUpdatedValue(def, stat, stuff);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static float GetStuffed(BuildableDef def, StatDef stat, ThingDef stuff)
|
||||
=> StatsCache.GetOrAdd(unchecked((int)DefPair.Create(def, stat)), stuff.shortHash).Value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static float TryGetUpdatedValue(BuildableDef def, StatDef stat, ThingDef? stuff)
|
||||
=> Current.Game is null ? GetValueSafely(def, stat, stuff) : GetUpdatedValue(def, stat, stuff);
|
||||
|
||||
private static float GetUpdatedValue(BuildableDef def, StatDef stat, ThingDef? stuff)
|
||||
{
|
||||
ref var statsCache = ref def.StatsCache();
|
||||
if (statsCache.Length != DefDatabase<StatDef>.DefCount)
|
||||
statsCache = InitializeBuildableDefStatCache();
|
||||
|
||||
ref var statBucket = ref stuff is null
|
||||
? ref statsCache[stat.index]
|
||||
: ref StatsCache.GetOrAddReference(unchecked((int)DefPair.Create(def, stat)), stuff.shortHash).Value;
|
||||
|
||||
return statBucket = GetValueSafely(def, stat, stuff);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static float GetValueSafely(BuildableDef def, StatDef stat, ThingDef? stuff = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return stat.Worker.GetValueAbstract(def, stuff);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatExceptionHandlers.GetOrAdd(stat)(def, stuff, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static float LogErrorInStat(BuildableDef def, ThingDef? stuff, Exception exception, string name)
|
||||
{
|
||||
Guard.IsNotNull(def);
|
||||
Log.Error($"Exception thrown while calculating {name} for def '{def.defName}' from mod '{
|
||||
def.modContentPack?.Name ?? "null"}'{(stuff != null ? $" with stuff '{stuff.defName}' from mod '{
|
||||
stuff.modContentPack?.Name ?? "null"}'" : "")}.\n{exception}");
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public static float[] InitializeBuildableDefStatCache()
|
||||
{
|
||||
TryVerifyIndices();
|
||||
var array = new float[DefDatabase<StatDef>.DefCount];
|
||||
ResetToDefaults(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static void ResetToDefaults(float[] statsCache) => Array.Fill(statsCache, float.NaN);
|
||||
|
||||
public static void ResetAllStatsCaches()
|
||||
{
|
||||
var resetAllStatsCachesForDefTypeBaseMethod
|
||||
= methodof(ResetAllStatsCachesForDefType<BuildableDef>).GetGenericMethodDefinition();
|
||||
|
||||
foreach (var defType in GenDefDatabase.AllDefTypesWithDatabases())
|
||||
{
|
||||
if (defType.IsAssignableTo(typeof(BuildableDef)))
|
||||
resetAllStatsCachesForDefTypeBaseMethod.MakeGenericMethod(defType).Invoke(null, null);
|
||||
}
|
||||
|
||||
StatsCache.Clear();
|
||||
}
|
||||
|
||||
public static void ResetAllStatsCachesForDefType<T>() where T : BuildableDef
|
||||
{
|
||||
var defsList = DefDatabase<T>.AllDefsListForReading;
|
||||
for (var i = 0; i < defsList.Count; i++)
|
||||
{
|
||||
var def = defsList[i];
|
||||
ResetToDefaults(def.StatsCache());
|
||||
|
||||
if (def is ThingDef thingDef)
|
||||
ResetToDefaults(thingDef.BaseStatsCache());
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryVerifyIndices()
|
||||
{
|
||||
if (_verifiedIndices)
|
||||
return;
|
||||
|
||||
VerifyIndices();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void VerifyIndices()
|
||||
{
|
||||
lock (_verifyIndicesLock)
|
||||
{
|
||||
if (Volatile.Read(ref _verifiedIndices))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
DefDatabase<StatDef>.SetIndices();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Volatile.Write(ref _verifiedIndices, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool _verifiedIndices;
|
||||
private static object _verifyIndicesLock = new();
|
||||
|
||||
public static FishTable<StatDef, Func<BuildableDef, ThingDef?, Exception, float>> StatExceptionHandlers
|
||||
= new() { ValueInitializer = static stat => (def, stuff, ex) => LogErrorInStat(def, stuff, ex, stat.label) };
|
||||
|
||||
public record struct CachedStatValue()
|
||||
{
|
||||
public float Value = float.NaN;
|
||||
}
|
||||
}
|
||||
@@ -415,11 +415,9 @@ public sealed class ReflectionCaching : ClassWithFishPatches
|
||||
public PropertyInfo? Info;
|
||||
}
|
||||
|
||||
public record struct TypeFullNameCacheValue
|
||||
public record struct TypeFullNameCacheValue()
|
||||
{
|
||||
private object? _value;
|
||||
|
||||
public TypeFullNameCacheValue() => _value = typeof(void);
|
||||
private object? _value = typeof(void);
|
||||
|
||||
public string? Name
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@ public sealed class ThingCompCaching : ClassWithFishPatches
|
||||
=> IdeoForbidsCache.GetExistingReference(__instance, pawn).Update(__instance, pawn, __result);
|
||||
}
|
||||
|
||||
public record struct IdeoForbidsCacheValue
|
||||
public record struct IdeoForbidsCacheValue()
|
||||
{
|
||||
public bool Result;
|
||||
private int _nextRefreshTick = -2;
|
||||
@@ -85,9 +85,5 @@ public sealed class ThingCompCaching : ClassWithFishPatches
|
||||
|
||||
private static readonly List<DirectPawnRelation> _nullRelations = [];
|
||||
private static readonly List<Pawn> _nullPawns = [];
|
||||
|
||||
public IdeoForbidsCacheValue()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,7 +205,7 @@ public sealed class ThingFilterPatches : ClassWithFishPatches
|
||||
public static void ForceSynchronizeCache(ThingFilter filter)
|
||||
=> AllowedDefsListCache.GetOrAddReference(filter).Update(ref filter);
|
||||
|
||||
public record struct AllowedDefsListCacheValue : ICacheable<ThingFilter>, ICacheable<ThingFilter, int>
|
||||
public record struct AllowedDefsListCacheValue() : ICacheable<ThingFilter>, ICacheable<ThingFilter, int>
|
||||
{
|
||||
public List<ThingDef> Defs = [];
|
||||
private int _nextRefreshTick = -2;
|
||||
@@ -229,7 +229,7 @@ public sealed class ThingFilterPatches : ClassWithFishPatches
|
||||
|
||||
Defs.ReplaceContentsWith(allowedDefs);
|
||||
|
||||
SortDefs(Defs); // why, actually? I don't remember
|
||||
SortDefs(Defs); // TODO: figure out what this was meant for
|
||||
|
||||
SetDirty(false, tickOffset + Defs.Count + (key.disallowedSpecialFilters?.Count ?? 0));
|
||||
}
|
||||
@@ -240,11 +240,11 @@ public sealed class ThingFilterPatches : ClassWithFishPatches
|
||||
var defArray = defs._items;
|
||||
var marketValues = GetTempArrayForSorting(defCount);
|
||||
for (var i = 0; i < defCount; i++)
|
||||
marketValues[i] = GetMarketValueSafely(defArray[i]);
|
||||
marketValues[i] = defArray[i].BaseMarketValue;
|
||||
|
||||
//Array.Sort(marketValues, defs); would require an array of perfect length, generating garbage
|
||||
// Array.Sort(marketValues, defs); has no count argument
|
||||
|
||||
defArray.AsSpan()[..defCount].Sort(marketValues.AsSpan()[..defCount], null);
|
||||
defArray.AsSpan()[..defCount].Sort(marketValues.AsSpan()[..defCount]);
|
||||
defs._version++;
|
||||
}
|
||||
|
||||
@@ -254,60 +254,5 @@ public sealed class ThingFilterPatches : ClassWithFishPatches
|
||||
: _tempArrayForSorting = new float[defCount];
|
||||
|
||||
private static float[] _tempArrayForSorting = Array.Empty<float>();
|
||||
|
||||
private static float GetMarketValueSafely(ThingDef def)
|
||||
{
|
||||
try
|
||||
{
|
||||
return def.BaseMarketValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return LogAndFixMarketValue(def, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static float LogAndFixMarketValue(ThingDef def, Exception exception)
|
||||
{
|
||||
Guard.IsNotNull(def);
|
||||
Log.Warning($"Exception thrown while calculating market value for def {def.defName} from mod {
|
||||
def.modContentPack?.Name ?? "null"}. Attempting to fix this now so it doesn't happen again.\n{
|
||||
exception}");
|
||||
|
||||
RecipeDef? recipe;
|
||||
try
|
||||
{
|
||||
recipe = StatWorker_MarketValue.CalculableRecipe(def);
|
||||
// this is slow, but gets cached by perf optimizer. Might be worth replicating
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Exception thrown while calculating recipe for def {def.defName} from mod {
|
||||
def.modContentPack?.Name ?? "null"}.\n{ex}");
|
||||
recipe = null;
|
||||
}
|
||||
|
||||
if (recipe?.ingredients is { } ingredients)
|
||||
ingredients.RemoveAll(static ingredientCount => ingredientCount is null);
|
||||
|
||||
if (def.CostList is { } costList)
|
||||
costList.RemoveAll(static thingDefCountClass => thingDefCountClass?.thingDef is null);
|
||||
|
||||
try
|
||||
{
|
||||
return def.BaseMarketValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Exception thrown while calculating market value for def {def.defName} from mod {
|
||||
def.modContentPack?.Name ?? "null"} again. Fix failed, F.\n{ex}");
|
||||
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public AllowedDefsListCacheValue()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ public sealed class ThingOwnerUtilityPreCaching : ClassWithFishPrepatches
|
||||
return __state = true;
|
||||
}
|
||||
|
||||
outThings.ReplaceContentsWith(cache.Things!);
|
||||
outThings.ReplaceContentsWith(cache.Things);
|
||||
return __state = false;
|
||||
}
|
||||
|
||||
@@ -73,11 +73,7 @@ public sealed class ThingOwnerUtilityPreCaching : ClassWithFishPrepatches
|
||||
ref var cache = ref Cache.ByInt<Map, ThingRequestGroup, ThingRequestInfo<T>>.Get.GetReference(new(map.uniqueID,
|
||||
(byte)request.group));
|
||||
|
||||
if (cache.Things is null)
|
||||
cache.Things = outThings.Copy();
|
||||
else
|
||||
cache.Things.ReplaceContentsWith(outThings);
|
||||
|
||||
cache.Things.ReplaceContentsWith(outThings);
|
||||
cache.ListsByGroupCount = map.listerThings.listsByGroup[(int)request.group]?.Count ?? -1;
|
||||
cache.ThingHolderCount = ThingHolderCount(map);
|
||||
cache.SetDirty(false, map.uniqueID + (int)request.group + cache.ListsByGroupCount + cache.ThingHolderCount);
|
||||
@@ -181,12 +177,12 @@ public sealed class ThingOwnerUtilityPreCaching : ClassWithFishPrepatches
|
||||
public bool Dirty => _nextRefreshTick < Current.Game.tickManager.TicksGame;
|
||||
}*/
|
||||
|
||||
public record struct ThingRequestInfo<T> where T : Thing
|
||||
public record struct ThingRequestInfo<T>() where T : Thing
|
||||
{
|
||||
private int _nextUpdateTick;
|
||||
public int ListsByGroupCount;
|
||||
public int ThingHolderCount;
|
||||
public List<T>? Things;
|
||||
private int _nextUpdateTick = -2;
|
||||
public int ListsByGroupCount = -2;
|
||||
public int ThingHolderCount = -2;
|
||||
public readonly List<T> Things = [];
|
||||
|
||||
public void SetDirty(bool value, int offset)
|
||||
=> _nextUpdateTick = value ? 0 : TickHelper.Add(3072, offset, 2048);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using SharedAltarCache
|
||||
= PerformanceFish.Cache.ByIndex<RimWorld.Ideo,
|
||||
= PerformanceFish.Cache.ByInt<RimWorld.Ideo,
|
||||
PerformanceFish.ThoughtWorker_Precept_AltarSharingOptimization.SharedAltarCacheValue>;
|
||||
|
||||
namespace PerformanceFish;
|
||||
@@ -13,6 +13,8 @@ public sealed class ThoughtWorker_Precept_AltarSharingOptimization : ClassWithFi
|
||||
{
|
||||
public sealed class SharedAltar_Patch : FirstPriorityFishPatch
|
||||
{
|
||||
public override bool Enabled => base.Enabled && ModsConfig.IdeologyActive;
|
||||
|
||||
public override string Description { get; }
|
||||
= "Caches results of ideology's shared altar thoughtworker and throttles it to wait a minimum of 128 ticks "
|
||||
+ "between refreshes. Quite impactful on performance";
|
||||
@@ -20,17 +22,17 @@ public sealed class ThoughtWorker_Precept_AltarSharingOptimization : ClassWithFi
|
||||
public override MethodBase TargetMethodInfo { get; } = AccessTools.Method(
|
||||
typeof(ThoughtWorker_Precept_AltarSharing), nameof(ThoughtWorker_Precept_AltarSharing.SharedAltar));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(ThoughtWorker_Precept_AltarSharing __instance, Pawn pawn, ref Thing? __result,
|
||||
out bool __state)
|
||||
{
|
||||
if (!pawn.IsSpawned())
|
||||
if (!pawn.IsSpawned() || pawn.Ideo is not { } ideo)
|
||||
{
|
||||
__result = null;
|
||||
return __state = false;
|
||||
}
|
||||
|
||||
ref var cache = ref SharedAltarCache.Get.GetReference(pawn.Ideo);
|
||||
|
||||
ref var cache = ref SharedAltarCache.GetOrAddReference(ideo.id);
|
||||
if (cache.Dirty)
|
||||
return __state = true;
|
||||
|
||||
@@ -38,22 +40,31 @@ public sealed class ThoughtWorker_Precept_AltarSharingOptimization : ClassWithFi
|
||||
return __state = false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Postfix(Pawn pawn, Thing __result, bool __state)
|
||||
{
|
||||
if (!__state)
|
||||
return;
|
||||
|
||||
SharedAltarCache.Get[pawn.Ideo] = new(pawn, __result);
|
||||
UpdateCache(pawn, __result);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void UpdateCache(Pawn pawn, Thing __result)
|
||||
=> SharedAltarCache.GetExistingReference(pawn.Ideo.id) = new(pawn, __result);
|
||||
}
|
||||
|
||||
public record struct SharedAltarCacheValue
|
||||
{
|
||||
public Thing? thing;
|
||||
private int _nextLateRefreshTick;
|
||||
private int _nextEarlyRefreshTick;
|
||||
private int _allStructuresListVersion;
|
||||
private ListerThings _lister;
|
||||
private int _nextLateRefreshTick = -2;
|
||||
private int _nextEarlyRefreshTick = -2;
|
||||
private int _allStructuresListVersion = -2;
|
||||
private ListerThings _lister = null!;
|
||||
|
||||
public SharedAltarCacheValue()
|
||||
{
|
||||
}
|
||||
|
||||
public SharedAltarCacheValue(Pawn pawn, Thing? result)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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/.
|
||||
|
||||
#if unused
|
||||
using PerformanceFish.Prepatching;
|
||||
|
||||
namespace PerformanceFish;
|
||||
|
||||
public sealed class TickManagerPatches : ClassWithFishPrepatches
|
||||
{
|
||||
public sealed class SetCurTimeSpeedPatch : FishPrepatch
|
||||
{
|
||||
public override bool ShowSettings => false;
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredPropertySetter(typeof(TickManager), nameof(TickManager.CurTimeSpeed));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Prefix(TickManager __instance, TimeSpeed value) => TimeSpeedChanging(__instance, value);
|
||||
}
|
||||
|
||||
public sealed class TogglePausedPatch : FishPrepatch
|
||||
{
|
||||
public override bool ShowSettings => false;
|
||||
|
||||
public override MethodBase TargetMethodBase { get; }
|
||||
= AccessTools.DeclaredMethod(typeof(TickManager), nameof(TickManager.TogglePaused));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Prefix(TickManager __instance) => TimeSpeedChanging(__instance, TimeSpeed.Paused);
|
||||
}
|
||||
|
||||
public static void TimeSpeedChanging(TickManager tickManager, TimeSpeed value)
|
||||
{
|
||||
if (value == tickManager.curTimeSpeed)
|
||||
return;
|
||||
|
||||
if (value == TimeSpeed.Paused)
|
||||
GamePausing();
|
||||
else if (tickManager.curTimeSpeed == TimeSpeed.Paused)
|
||||
GameResuming();
|
||||
}
|
||||
|
||||
public static void GamePausing()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void GameResuming()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -125,6 +125,9 @@ public static class MiscExtensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsSpawned(this Thing thing) => (uint)thing.mapIndexOrState < (uint)Current.Game.Maps.Count;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetLoadID(this Bill bill) => bill.loadID;
|
||||
|
||||
public static void InvokeWhenCellIndicesReady(this Map map, Action<Map> action)
|
||||
{
|
||||
if (map.cellIndices != null)
|
||||
|
||||
@@ -3,15 +3,10 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace PerformanceFish.Utility;
|
||||
|
||||
public record struct PooledArray<T> : IDisposable, IEnumerable<T>
|
||||
{
|
||||
private static T[]?[] _buffers = new T[]?[4];
|
||||
private static int _bufferCount;
|
||||
|
||||
private T[] _array;
|
||||
private int _length;
|
||||
|
||||
@@ -19,6 +14,16 @@ public record struct PooledArray<T> : IDisposable, IEnumerable<T>
|
||||
public int Length => _length;
|
||||
public int Capacity => _array.Length;
|
||||
|
||||
[ThreadStatic]
|
||||
private static T[]?[]? _buffersThreadStatic;
|
||||
|
||||
[ThreadStatic]
|
||||
private static int _bufferCountThreadStatic;
|
||||
|
||||
private static object _lockObject = new();
|
||||
private static T[]?[] _buffersGlobal = new T[]?[16];
|
||||
private static int _bufferCountGlobal;
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -41,22 +46,21 @@ public record struct PooledArray<T> : IDisposable, IEnumerable<T>
|
||||
|
||||
public PooledArray(int length)
|
||||
{
|
||||
if (_bufferCount < 1)
|
||||
{
|
||||
NewBuffer(length);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref var bucket = ref _buffers[--_bufferCount];
|
||||
_array = bucket!;
|
||||
bucket = null;
|
||||
if (_array.Length < length)
|
||||
ResizeBuffer(length);
|
||||
}
|
||||
if (_bufferCountThreadStatic == 0)
|
||||
FetchThroughLock();
|
||||
|
||||
ref var bucket = ref _buffersThreadStatic![--_bufferCountThreadStatic];
|
||||
_array = bucket!;
|
||||
bucket = null;
|
||||
if (_array.Length < length)
|
||||
ResizeBuffer(length);
|
||||
|
||||
_length = length;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void ResizeBuffer(int length) => Array.Resize(ref _array, Mathf.NextPowerOfTwo(length));
|
||||
|
||||
public PooledArray(ICollection<T> collection) : this(collection.Count) => collection.CopyTo(_array, 0);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -66,35 +70,78 @@ public record struct PooledArray<T> : IDisposable, IEnumerable<T>
|
||||
return ref _array[index];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void ResizeBuffer(int length) => Array.Resize(ref _array, Mathf.NextPowerOfTwo(length));
|
||||
|
||||
[MemberNotNull(nameof(_array))]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void NewBuffer(int length) => _array = new T[Mathf.NextPowerOfTwo(length)];
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (++_bufferCount > _buffers.Length)
|
||||
ExpandBuffers(_bufferCount);
|
||||
|
||||
if (_length > 0)
|
||||
Array.Clear(_array, 0, _length);
|
||||
|
||||
_buffers[_bufferCount - 1] = _array;
|
||||
var buffersThreadStatic = _buffersThreadStatic ??= InitializeBuffersThreadStatic();
|
||||
|
||||
if (_bufferCountThreadStatic == 16)
|
||||
PushThroughLock();
|
||||
|
||||
buffersThreadStatic[_bufferCountThreadStatic++] = _array;
|
||||
}
|
||||
|
||||
public void Sort(IComparer<T> comparer) => _array.AsSpan()[.._length].Sort(comparer);
|
||||
|
||||
private static void ExpandBuffers(int minLength)
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void FetchThroughLock()
|
||||
{
|
||||
var newBuffers = new T[]?[Mathf.NextPowerOfTwo(minLength)];
|
||||
var buffersThreadStatic = _buffersThreadStatic ??= InitializeBuffersThreadStatic();
|
||||
var createNew = false;
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_bufferCountGlobal != 0)
|
||||
{
|
||||
for (var i = 8; i-- > 0;)
|
||||
{
|
||||
buffersThreadStatic[i] = _buffersGlobal[--_bufferCountGlobal];
|
||||
_buffersGlobal[_bufferCountGlobal] = default;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
createNew = true;
|
||||
}
|
||||
}
|
||||
|
||||
Array.Copy(_buffers, newBuffers, _buffers.Length);
|
||||
|
||||
_buffers = newBuffers;
|
||||
if (createNew)
|
||||
Array.Fill(buffersThreadStatic, Array.Empty<T>(), 0, 8);
|
||||
|
||||
_bufferCountThreadStatic = 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void PushThroughLock()
|
||||
{
|
||||
var buffersThreadStatic = _buffersThreadStatic!;
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_bufferCountGlobal + 8 > _buffersGlobal.Length)
|
||||
ExpandGlobalBuffers();
|
||||
|
||||
for (var i = 16; i-- > 8;)
|
||||
{
|
||||
ref var bucket = ref buffersThreadStatic[i];
|
||||
var buffer = bucket;
|
||||
bucket = default;
|
||||
|
||||
_buffersGlobal[_bufferCountGlobal++] = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
_bufferCountThreadStatic = 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ExpandGlobalBuffers() => Array.Resize(ref _buffersGlobal, _buffersGlobal.Length << 1);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static T[]?[] InitializeBuffersThreadStatic() => new T[]?[16];
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < _length; i++)
|
||||
|
||||
@@ -10,6 +10,18 @@ public static class StringExtensions
|
||||
public static bool NameEqualsCaseInsensitive(this MethodInfo methodInfo, string name)
|
||||
=> methodInfo.Name.Equals(name, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public static string AppendNewLineWhen(this string instance, bool predicate, string text)
|
||||
=> predicate ? instance.AppendNewLine(text) : instance;
|
||||
|
||||
public static string AppendNewLineWhenNotNull(this string instance, object? value)
|
||||
=> value != null ? instance.AppendNewLine(value.ToString()) : instance;
|
||||
|
||||
public static string AppendNewLine(this string instance, string text)
|
||||
=> string.Concat(instance, "\n", text);
|
||||
|
||||
public static string AppendWhen(this string instance, bool predicate, string text)
|
||||
=> predicate ? instance + text : instance;
|
||||
|
||||
public static string AppendWhenNotNull(this string instance, object? value)
|
||||
=> value != null ? instance + value : instance;
|
||||
}
|
||||
@@ -49,6 +49,7 @@ public sealed class UtilityPatches : ClassWithFishPatches
|
||||
public static void Postfix()
|
||||
{
|
||||
Cache.Utility.Clear();
|
||||
StatCaching.ResetAllStatsCaches();
|
||||
ParallelNoAlloc.ClearAll();
|
||||
foreach (var patch in PerformanceFishMod.AllPatchClasses!)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user