Added gas grid optimization, various despawn optimizations, new hauling optimizations utilizing events and regions, bugfixes for old patches and for vanilla functions too, as well as smaller additions. 1.3 files removed.

This commit is contained in:
bbradson
2024-01-27 10:24:24 +01:00
parent 0247ad59a1
commit c8b764de68
151 changed files with 7318 additions and 2145 deletions
@@ -0,0 +1,50 @@
// 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/.
// crashes to desktop somehow
/*using Mono.Cecil;
using Mono.Cecil.Cil;
using PerformanceFish.Prepatching;
namespace PerformanceFish.Defs;
public sealed class DefDatabasePatches : ClassWithFishPrepatches
{
public sealed class GetNamedPatch : FishPrepatch
{
public override MethodBase TargetMethodBase { get; } = methodof(DefDatabase<Def>.GetNamed);
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
=> ilProcessor.ReplaceBodyWith(GetNamed<Def>);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? GetNamed<T>(string defName, bool errorOnFail = true) where T : Def
{
var value = DefDatabase<T>.defsByName.TryGetValue(defName);
if (errorOnFail && value is null)
LogMissingDef<T>(defName);
return value;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogMissingDef<T>(string defName) where T : Def
=> Log.Error($"Failed to find {typeof(T)} named {defName}. There are {
DefDatabase<T>.defsList.Count} defs of this type loaded.");
}
public sealed class GetByShortHashPatch : FishPrepatch
{
public override MethodBase TargetMethodBase { get; } = methodof(DefDatabase<Def>.GetByShortHash);
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
=> ilProcessor.ReplaceBodyWith(GetByShortHash<Def>);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? GetByShortHash<T>(ushort shortHash) where T : Def
=> DefDatabase<T>.defsByShortHash.TryGetValue(shortHash);
}
}*/
@@ -0,0 +1,18 @@
// 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.Defs;
public record struct DefThingPair(int DefShortHash, int ThingIDNumber)
{
public int
DefShortHash = DefShortHash,
ThingIDNumber = ThingIDNumber;
public bool Equals(DefThingPair other)
=> Unsafe.As<DefThingPair, ulong>(ref this) == Unsafe.As<DefThingPair, ulong>(ref other);
public override int GetHashCode() => HashCode.Combine(DefShortHash, ThingIDNumber);
}
+27
View File
@@ -0,0 +1,27 @@
// 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 System.Linq;
namespace PerformanceFish.Defs;
[StaticConstructorOnStartup]
public static class FishDefOfOrNull
{
public static RecipeDef VFEA_MendWeapon { get; }
public static RecipeDef VFEA_MendArmor { get; }
public static RecipeDef VFEA_MendApparel { get; }
static FishDefOfOrNull()
{
foreach (var property in typeof(FishDefOfOrNull).GetProperties())
{
typeof(FishDefOfOrNull).GetFields(BindingFlags.Static | BindingFlags.NonPublic).First(f => f.Name.Contains(property.Name)).SetValue(null,
typeof(FishDefOfOrNull).GetMethod(nameof(DefOf), BindingFlags.Static | BindingFlags.NonPublic)
.MakeGenericMethod(property.GetMethod.ReturnType).Invoke(null, new[] { property.Name }));
}
}
private static T DefOf<T>(string name) where T : Def => DefDatabase<T>.GetNamedSilentFail(name);
}*/
@@ -0,0 +1,144 @@
// 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 RecipeUsersCache
= PerformanceFish.Cache.ByInt<Verse.RecipeDef,
PerformanceFish.Defs.RecipeDefCaching.AllRecipeUsers_Patch.RecipeUsersCacheValue>;
namespace PerformanceFish.Defs;
public sealed class RecipeDefCaching : ClassWithFishPatches
{
public override bool RequiresLoadedGameForPatching => true;
public sealed class AllRecipeUsers_Patch : FirstPriorityFishPatch
{
public override string Description { get; }
= "Caches results of the AllRecipeUsers method. Becomes impactful with lots of unfinished things around, "
+ "as those check everything for potentially accepting them";
public override MethodBase TargetMethodInfo { get; }
= AccessTools.PropertyGetter(typeof(RecipeDef), nameof(RecipeDef.AllRecipeUsers));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Prefix(RecipeDef? __instance, ref IEnumerable<ThingDef> __result, out bool __state)
{
if (__instance is null)
{
__state = false;
return true;
}
ref var cache = ref RecipeUsersCache.GetOrAddReference(__instance.shortHash);
if (cache.Dirty)
return __state = true;
__result = cache.RecipeUsers;
return __state = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Postfix(RecipeDef __instance, IEnumerable<ThingDef> __result, bool __state)
{
if (!__state)
return;
UpdateCache(__instance, __result);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void UpdateCache(RecipeDef __instance, IEnumerable<ThingDef> __result)
=> RecipeUsersCache.GetExistingReference(__instance).Update(__instance, __result);
public record struct RecipeUsersCacheValue
{
private int _defDatabaseVersion;
private int _defRecipeUsersVersion;
private RecipeDef _def;
public List<ThingDef> RecipeUsers;
public void Update(RecipeDef instance, IEnumerable<ThingDef> result)
{
if (RecipeUsers is { } list)
{
list.Clear();
list.AddRange(result);
}
else
{
RecipeUsers = [..result];
}
_def = instance;
_defDatabaseVersion = DefDatabase<ThingDef>.defsList._version;
_defRecipeUsersVersion = instance.recipeUsers?._version ?? 0;
}
public bool Dirty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
=> _defDatabaseVersion != DefDatabase<ThingDef>.defsList._version
|| _defRecipeUsersVersion != (_def.recipeUsers?._version ?? 0);
}
}
}
/*public sealed class PotentiallyMissingIngredients_Patch : FirstPriorityFishPatch
{
public override Expression<Action> TargetMethod { get; }
= () => new RecipeDef().PotentiallyMissingIngredients(null, null);
public static bool Prefix(RecipeDef __instance, Pawn billDoer, Map map, ref IEnumerable<ThingDef> __result)
{
if (!Cache.ByReference<RecipeDef, Pawn, Map, MissingIngredientsCache>.TryGetValue(__instance, billDoer, map,
out var cache))
return true;
if (__instance.ingredients.Count != 0)
{
Log.Warning("RecipeDef ingredients mismatched its cache in Performance Fish, forcing a refresh");
return true;
}
__result = cache.ingredientThingDefs;
return false;
}
public static void Postfix(RecipeDef __instance, Pawn billDoer, Map map, IEnumerable<ThingDef> __result,
bool __runOriginal)
{
if (!__runOriginal)
return;
Cache.ByReference<RecipeDef, Pawn, Map, MissingIngredientsCache>.Get[new(__instance, billDoer, map)]
= new()
{
ingredientThingDefs = __result.ToArray(),
firstIngredientsItemCount = __instance.ingredients.FirstOrDefault()?.count ?? 0,
ShouldRefreshNow = false
};
}
public static MissingIngredientsCache GetCache(RecipeDef def, Pawn billDoer, Map map)
=> Cache.ByReference<RecipeDef, Pawn, Map, MissingIngredientsCache>.GetValue(def, billDoer, map);
public record struct MissingIngredientsCache
{
public ThingDef[] ingredientThingDefs;
private int _nextRefreshFrame;
public int firstIngredientsItemCount;
public bool ShouldRefreshNow
{
get => Time.frameCount > _nextRefreshFrame;
set => _nextRefreshFrame = value ? 0 : Time.frameCount + 2048 + Math.Abs(Rand.Int % 1024);
}
public MissingIngredientsCache SetNewValue(Cache<RecipeDef, Pawn, Map, MissingIngredientsCache> key)
=> throw new NotImplementedException();
}
}*/
}
@@ -0,0 +1,112 @@
// 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 System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using PerformanceFish.Prepatching;
using FieldAttributes = Mono.Cecil.FieldAttributes;
using MethodImplAttributes = Mono.Cecil.MethodImplAttributes;
namespace PerformanceFish.Defs;
public sealed class ThingDefPatches : ClassWithFishPrepatches
{
public const string FISH_HASH_FIELD_NAME = "FishHash";
public sealed class GetHashCodePatch : FishClassPrepatch
{
public override string? Description { get; }
= "Def.GetHashCode is overridden to return defName.GetHashCode, which is rather slow as that's a string. "
+ "This patch caches the hashcode in a field added through prepatcher and simply returns that instead.";
public override Type Type { get; } = typeof(BuildableDef);
public override void FreePatch(TypeDefinition typeDefinition)
{
var module = typeDefinition.Module;
var fishHashField = new FieldDefinition(FISH_HASH_FIELD_NAME, FieldAttributes.Private,
module.TypeSystem.Int32);
typeDefinition.Fields.Insert(0, fishHashField);
var constructorBody = typeDefinition.GetConstructors().First().Body;
constructorBody.GetILProcessor().InsertRange(constructorBody.Instructions.Count - 2,
OpCodes.Ldarg_0,
(OpCodes.Ldc_I4, -42),
(OpCodes.Stfld, fishHashField));
constructorBody.OptimizeMacros();
var getHashCodeBody = typeDefinition.GetMethod(nameof(BuildableDef.GetHashCode))!.Body;
var variables = getHashCodeBody.Variables;
variables.Clear();
var hashVariable = new VariableDefinition(module.TypeSystem.Int32);
variables.Add(hashVariable);
var instructions = getHashCodeBody.Instructions;
instructions.Clear();
var ilProcessor = getHashCodeBody.GetILProcessor();
ilProcessor.InsertRange(0,
OpCodes.Ldarg_0,
(OpCodes.Ldfld, fishHashField),
(OpCodes.Stloc, hashVariable),
(OpCodes.Ldloc, hashVariable),
(OpCodes.Ldc_I4, -42),
// 5 bne_un_s ^2
OpCodes.Ldarg_0,
(OpCodes.Call, methodof(CreateLongHash)),
OpCodes.Ret,
(OpCodes.Ldloc, hashVariable),
OpCodes.Ret);
ilProcessor.InsertAt(5, OpCodes.Bne_Un_S, instructions[^2]);
getHashCodeBody.MaxStackSize = 2;
getHashCodeBody.Method.ImplAttributes |= MethodImplAttributes.AggressiveInlining;
getHashCodeBody.OptimizeMacros();
}
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// public static int ReplacementBody(BuildableDef instance)
// {
// var hash = instance.FishHash;
// return hash != -42 ? hash : CreateLongHash(instance);
// }
[MethodImpl(MethodImplOptions.NoInlining)]
public static int CreateLongHash(BuildableDef instance)
{
var defName = instance.defName;
if (defName is null)
return 0;
var defHash = defName.GetHashCode();
if (defName is "" or Def.DefaultDefName)
return defHash;
ref var cacheHash = ref Lazy.FishHashField(instance);
cacheHash = defHash != -42 ? defHash : defHash + 1;
return cacheHash;
}
private static class Lazy
{
internal static readonly AccessTools.FieldRef<BuildableDef, int> FishHashField
= AccessTools.FieldRefAccess<BuildableDef, int>(FISH_HASH_FIELD_NAME);
static Lazy()
{
// prevent beforefieldinit
}
}
}
}