1.5, added stuff, fixed stuff

- mod icon
- fixes for new vanilla clipping issues
- GenClosest optimization improving repairing, wardening, tending jobs
- caching for new ListerBuildings methods
- DynamicDrawManager single threading
- Type.GetFields caching
- StatParts analyzer category
- removed duplicate steam warning and optionally window
- added exception handling for debug menu, incidentworker, Graphic_Multi, InitializeComps, making all those now log what mod broke and not cause black screens or whatever
This commit is contained in:
bbradson
2024-04-27 22:31:07 +02:00
parent be3dae46bb
commit d34bc77869
76 changed files with 3061 additions and 1134 deletions
@@ -3,6 +3,7 @@
// 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 V1_4
using PerformanceFish.ModCompatibility;
namespace PerformanceFish.JobSystem;
@@ -27,4 +28,5 @@ public sealed class DisabledWorkTypesOptimization : ClassWithFishPatches
? __instance.cachedDisabledWorkTypesPermanent
: __instance.cachedDisabledWorkTypes) is null;
}
}
}
#endif
@@ -3,227 +3,27 @@
// 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 disabledForNow
using System.Threading.Tasks;
using Verse.AI;
using PerformanceFish.Prepatching;
namespace PerformanceFish.JobSystem;
public sealed class GenClosestPatches : ClassWithFishPatches
public sealed class GenClosestPatches : ClassWithFishPrepatches
{
public sealed class ClosestThing_Global_Patch : FishPatch
public sealed class ClosestThingReachablePatch : FishPrepatch
{
public override string Description { get; } = "Threading for job scanning. Disabled and unstable.";
public override bool DefaultState => false;
public static bool ThreadingEnabled { get; set; }
public override string Description { get; }
= "Job scanning optimization making repairing, wardening and tending use a cache of targets where "
+ "appropriate";
public override MethodBase TargetMethodBase => methodof(GenClosest.ClosestThingReachable);
public override Expression<Action> TargetMethod { get; }
= static () => GenClosest.ClosestThing_Global(default, null, default, null, null);
public static CodeInstructions Transpiler(CodeInstructions CodeInstructions)
=> Reflection.MakeReplacementCall(ClosestThing_Global_Replacement);
private struct Variables
public static void Prefix(Map map, ref ThingRequest thingReq, IEnumerable<Thing>? customGlobalSearchSet)
{
public IntVec3 center;
public float maxDistanceSquared;
public Func<Thing, float>? priorityGetter;
public float closestDistSquared;
public Predicate<Thing>? validator;
}
if (!thingReq.CanBeFoundInRegion || customGlobalSearchSet is not ICollection<Thing> collection)
return;
public static Thing? ClosestThing_Global_Replacement(IntVec3 center, IEnumerable? searchSet,
float maxDistance = 99999f, Predicate<Thing>? validator = null, Func<Thing, float>? priorityGetter = null)
{
if (searchSet == null)
return null;
var closestDistSquared = 2.14748365E+09f;
Thing? chosen = null;
var bestPrio = float.MinValue;
//float maxDistanceSquared = maxDistance * maxDistance;
Variables variables = new()
{
center = center,
maxDistanceSquared = maxDistance * maxDistance,
validator = validator,
priorityGetter = priorityGetter,
closestDistSquared = closestDistSquared
};
var predicate = GenClosest_Predicate.GetValidator(validator?.Target)?.Target ?? validator?.Target;
if (ThreadingEnabled
&& validator != null
&& JobGiver_Work_Predicate.GetScanner(predicate) is WorkGiver_DoBill
/*or WorkGiver_ConstructDeliverResources or WorkGiver_ConstructFinishFrames*/
)
{
if (searchSet is IList<Thing> thingList)
{
Log.Message($"Running threading code for {validator.Target.GetType()}");
Exception? exception = null;
try
{
Parallel.For<(IList<Thing> list, Variables vars)>(0, thingList.Count,
() => (thingList, variables),
(i, loop, listAndVars) => (listAndVars.list,
ProcessThreaded(listAndVars.list[i], listAndVars.vars)), static _ => { });
}
catch (Exception ex)
{
exception = ex;
Log.Error($"Caught error while threading:\n{ex}");
}
if (exception is null)
Log.Message("Finished threading code without errors");
return chosen;
}
}
switch (searchSet)
{
case IList<Thing> thingList:
{
for (var i = 0; i < thingList.Count; i++)
Process(thingList[i], variables);
break;
}
case IList<Pawn> pawnList:
{
for (var j = 0; j < pawnList.Count; j++)
Process(pawnList[j], variables);
break;
}
case IList<Building> buildingList:
{
for (var k = 0; k < buildingList.Count; k++)
Process(buildingList[k], variables);
break;
}
case IList<IAttackTarget> attackTargetList:
{
for (var l = 0; l < attackTargetList.Count; l++)
Process((Thing)attackTargetList[l], variables);
break;
}
default:
{
foreach (Thing item in searchSet)
Process(item, variables);
break;
}
}
return chosen;
void Process<T>(T t, Variables variables) where T : Thing
{
if (!t.Spawned)
return;
float num = (variables.center - t.Position).LengthHorizontalSquared;
if (!(num > variables.maxDistanceSquared)
&& (variables.priorityGetter != null || num < closestDistSquared)
&& (variables.validator == null || variables.validator(t)))
{
var num2 = 0f;
if (variables.priorityGetter != null)
{
num2 = variables.priorityGetter(t);
if (num2 < bestPrio || (Math.Abs(num2 - bestPrio) < 0.001f && num >= closestDistSquared))
return;
}
chosen = t;
closestDistSquared = variables.closestDistSquared = num;
bestPrio = num2;
}
}
Variables ProcessThreaded<T>(T t, Variables variables) where T : Thing
{
if (!t.Spawned)
return variables;
float num = (variables.center - t.Position).LengthHorizontalSquared;
if (num > variables.maxDistanceSquared
|| (variables.priorityGetter == null
&& (!(num < variables.closestDistSquared)
|| !(num < (variables.closestDistSquared = closestDistSquared))))
|| (variables.validator != null && !variables.validator(t)))
{
return variables;
}
lock (Lock)
{
var num2 = 0f;
if (variables.priorityGetter != null)
{
num2 = variables.priorityGetter(t);
if (num2 < bestPrio || (Math.Abs(num2 - bestPrio) < 0.001f && num >= closestDistSquared))
return variables;
}
else
{
if (num >= closestDistSquared)
return variables;
}
chosen = t;
closestDistSquared = variables.closestDistSquared = num;
bestPrio = num2;
}
return variables;
}
}
private static object Lock { get; } = new();
private static Type JobGiverWorkPredicateType { get; } = AccessTools.FirstInner(typeof(JobGiver_Work),
t => AccessTools.Field(t, "scanner") != null
&& AccessTools.FirstMethod(t, m => m.Name.Contains("TryIssueJobPackage")) != null);
private static JobGiver_Work_PredicateAccess JobGiver_Work_Predicate { get; }
= (JobGiver_Work_PredicateAccess)Activator.CreateInstance(
typeof(JobGiver_Work_PredicateAccessImplementation<>).MakeGenericType(JobGiverWorkPredicateType));
private static Type GenClosestPredicateType { get; } = AccessTools.FirstInner(typeof(GenClosest),
t => AccessTools.Field(t, "validator") != null
&& AccessTools.FirstMethod(t, m => m.Name.Contains("ClosestThingReachable")) != null);
private static GenClosest_PredicateAccess GenClosest_Predicate { get; }
= (GenClosest_PredicateAccess)Activator.CreateInstance(
typeof(GenClosest_PredicateAccessImplementation<>).MakeGenericType(GenClosestPredicateType));
private abstract class JobGiver_Work_PredicateAccess
{
public abstract WorkGiver_Scanner? GetScanner(object? obj);
}
private class JobGiver_Work_PredicateAccessImplementation<T> : JobGiver_Work_PredicateAccess
{
public override WorkGiver_Scanner? GetScanner(object? obj) => obj is T t ? Scanner(t) : null;
public static AccessTools.FieldRef<T, WorkGiver_Scanner> Scanner { get; }
= AccessTools.FieldRefAccess<T, WorkGiver_Scanner>("scanner");
}
private abstract class GenClosest_PredicateAccess
{
public abstract Predicate<Thing>? GetValidator(object? obj);
}
private class GenClosest_PredicateAccessImplementation<T> : GenClosest_PredicateAccess
{
public override Predicate<Thing>? GetValidator(object? obj) => obj is T t ? Validator(t) : null;
public static AccessTools.FieldRef<T, Predicate<Thing>> Validator { get; }
= AccessTools.FieldRefAccess<T, Predicate<Thing>>("validator");
if (map.listerThings.ThingsMatching(thingReq).Count > collection.Count * 5)
thingReq = ThingRequest.ForUndefined();
}
}
#endif
//}
}
@@ -3,13 +3,8 @@
// 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.Collections.Concurrent;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
/*using System.Linq;
using static System.Reflection.Emit.OpCodes;
using static PerformanceFish.TranspilerHelpers;
namespace PerformanceFish.JobSystem;
@@ -17,9 +12,17 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
{
public sealed class TryIssueJobPackage_InnerDelegate_Patch : FishPatch
{
public override string Description => "Work scanning optimization. Splits the IsForbidden and HasJobOnThing checks into separate loops, then threads the IsForbidden loop, sorts it, caches the result and skips any following duplicate checks";
public override MethodBase TargetMethodInfo => AccessTools.FindIncludingInnerTypes(typeof(JobGiver_Work),
t => AccessTools.Field(t, "scanner") != null ? AccessTools.FirstMethod(t, m => m.Name.Contains("TryIssueJobPackage") && m.ReturnType == typeof(bool)) : null);
public override string Description { get; }
= "Work scanning optimization. Splits the IsForbidden and HasJobOnThing checks into separate loops, then "
+ "sorts the IsForbidden loop, caches the result and skips any following duplicate checks";
public override MethodBase? TargetMethodInfo { get; }
= AccessTools.FindIncludingInnerTypes(typeof(JobGiver_Work), static t
=> AccessTools.Field(t, "scanner") != null
? AccessTools.FirstMethod(t,
static m => m.Name.Contains("TryIssueJobPackage") && m.ReturnType == typeof(bool))
: null);
public static CodeInstructions Transpiler(CodeInstructions CodeInstructions)
{
var shouldRemove = true;
@@ -46,29 +49,40 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
public sealed class TryIssueJobPackage_Patch : FishPatch
{
public override string Description => "Work scanning optimization. Splits the IsForbidden and HasJobOnThing checks into separate loops, then threads the IsForbidden loop, sorts it, caches the result and skips any following duplicate checks";
public override MethodBase TargetMethodInfo => AccessTools.Method(typeof(JobGiver_Work), nameof(JobGiver_Work.TryIssueJobPackage));
public override string Description { get; }
= "Work scanning optimization. Splits the IsForbidden and HasJobOnThing checks into separate loops, "
+ "then sorts it, caches the result and skips any following duplicate checks";
public override MethodBase TargetMethodInfo
=> AccessTools.Method(typeof(JobGiver_Work), nameof(JobGiver_Work.TryIssueJobPackage));
public static void Prefix(Pawn pawn)
{
PrefilteredPotentialWorkThingsGlobal.Clear();
FishCache.ClearAll();
CurrentPawn = pawn;
}
public static void Postfix()
{
PrefilteredPotentialWorkThingsGlobal.Clear();
FishCache.ClearAll();
CurrentPawn = null;
}
public static CodeInstructions Transpiler(CodeInstructions CodeInstructions, MethodBase original)
{
var pawn = GetLdarg(original, "pawn");
var pawn = FishTranspiler.Argument(original, "pawn");
var scanner_pawn_PotentialWorkThingsGlobal = GetCallvirt(typeof(WorkGiver_Scanner), nameof(WorkGiver_Scanner.PotentialWorkThingsGlobal));
var potentialWorkThingsGlobal_pawn_TryGetPrefilteredPotentialWorkThingsAnyType = GetCall(() => TryGetPrefilteredPotentialWorkThingsAnyType(null, null));
var scanner_pawn_PotentialWorkThingsGlobal = FishTranspiler.Call(typeof(WorkGiver_Scanner),
nameof(WorkGiver_Scanner.PotentialWorkThingsGlobal));
var potentialWorkThingsGlobal_pawn_TryGetPrefilteredPotentialWorkThingsAnyType
= FishTranspiler.Call(static () => TryGetPrefilteredPotentialWorkThingsAnyType(null!, null!));
var scanner_PotentialWorkThingRequest = GetGetPropertyCallvirt(typeof(WorkGiver_Scanner), nameof(WorkGiver_Scanner.PotentialWorkThingRequest));
var scanner_pawn_GetCachedThingRequest = GetCall(() => GetCachedThingRequest(null, null));
var scanner_PotentialWorkThingRequest = FishTranspiler.PropertyGetter(typeof(WorkGiver_Scanner),
nameof(WorkGiver_Scanner.PotentialWorkThingRequest));
var scanner_pawn_GetCachedThingRequest
= FishTranspiler.Call(static () => GetCachedThingRequest(null!, null!));
foreach (var code in CodeInstructions)
{
@@ -97,31 +111,35 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
return originalRequest;
var listerThings = pawn.Map.listerThings.ThingsMatching(originalRequest);
var collection = listerThings.Count > 100 ? (scanner.PotentialWorkThingsGlobal(pawn) as ICollection<Thing>) : null;
var collection = listerThings.Count > 100
? scanner.PotentialWorkThingsGlobal(pawn) as ICollection<Thing>
: null;
var processedThings = TryGetPrefilteredPotentialWorkThingsAnyType(collection is null || listerThings.Count < collection.Count * 4 ? listerThings : collection, pawn);
var fishCache = originalRequest.singleDef != null ? FishCache.ForDef(originalRequest.singleDef) : FishCache.ForGroup(originalRequest.group);
fishCache.FilteredThings = processedThings as List<Thing> ?? new(processedThings);
return new ThingRequest() { group = originalRequest.group, singleDef = fishCache };
var processedThings = TryGetPrefilteredPotentialWorkThingsAnyType(
collection is null || listerThings.Count < collection.Count * 4 ? listerThings : collection, pawn);
var fishCache = originalRequest.singleDef != null
? FishCache.ForDef(originalRequest.singleDef)
: FishCache.ForGroup(originalRequest.group);
fishCache.FilteredThings = processedThings as List<Thing> ?? [..processedThings];
return new() { group = originalRequest.group, singleDef = fishCache };
}
public static IEnumerable<Thing> TryGetPrefilteredPotentialWorkThingsAnyType(IEnumerable<Thing> originalWorkThings, Pawn pawn)
public static IEnumerable<Thing> TryGetPrefilteredPotentialWorkThingsAnyType(
IEnumerable<Thing> originalWorkThings, Pawn pawn)
=> originalWorkThings switch
{
List<Thing> => Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(originalWorkThings, pawn),
List<Pawn> => Generic<Pawn>.TryGetPrefilteredPotentialWorkThingsForList(originalWorkThings, pawn),
_ => GetPrefilteredPotentialWorkThingsUncached(originalWorkThings, pawn)
};
public static IEnumerable<Thing> GetPrefilteredPotentialWorkThingsUncached(IEnumerable<Thing> originalWorkThings, Pawn pawn)
=> (originalWorkThings is not ICollection<Thing> collection || collection.Count < 2048)
? CheckForbidden(originalWorkThings, pawn)
: Generic<Thing>.Threaded_CheckForbiddenAndAddToAnyCollection(collection, pawn);
public static IEnumerable<Thing> CheckForbidden(IEnumerable<Thing> things, Pawn pawn)
public static IEnumerable<Thing>
GetPrefilteredPotentialWorkThingsUncached(IEnumerable<Thing>? originalWorkThings, Pawn pawn)
{
if (things is null)
if (originalWorkThings is null)
yield break;
foreach (var thing in things)
foreach (var thing in originalWorkThings)
{
if (!thing.IsForbidden(pawn))
yield return thing;
@@ -130,7 +148,8 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
public static class Generic<T> where T : Thing
{
public static IEnumerable<Thing> TryGetPrefilteredPotentialWorkThingsForList(IEnumerable<Thing> originalWorkThings, Pawn pawn)
public static IEnumerable<Thing> TryGetPrefilteredPotentialWorkThingsForList(
IEnumerable<Thing> originalWorkThings, Pawn pawn)
{
if (!originalWorkThings.Any())
return originalWorkThings;
@@ -140,19 +159,20 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
return cache;
}
public static T[] RecalculatePrefilteredPotentialWorkThings(IEnumerable<Thing> originalWorkThings, Pawn pawn)
public static T[] RecalculatePrefilteredPotentialWorkThings(IEnumerable<Thing> originalWorkThings,
Pawn pawn)
{
var inputList = originalWorkThings as List<T>;
if (inputList.Count < 512)
Sequential_CheckForbiddenAndAddToList(inputList, pawn);
else
Threaded_CheckForbiddenAndAddToList(inputList, pawn);
var inputList = (List<T>)originalWorkThings;
Sequential_CheckForbiddenAndAddToList(inputList, pawn);
var cache = _tempThingList.ToArray();
if (cache.Length < 1024)
{
var pawnPosition = pawn.Position;
Array.Sort(cache, (a, b) => (pawnPosition - a.Position).LengthHorizontalSquared.CompareTo((pawnPosition - b.Position).LengthHorizontalSquared));
Array.Sort(cache,
(a, b) => (pawnPosition - a.Position).LengthHorizontalSquared.CompareTo(
(pawnPosition - b.Position).LengthHorizontalSquared));
}
PrefilteredPotentialWorkThingsGlobal[originalWorkThings] = cache;
@@ -160,6 +180,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
return cache;
}
private static void Sequential_CheckForbiddenAndAddToList(List<T> inputList, Pawn pawn)
{
_tempThingList.Clear();
@@ -171,82 +192,20 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
tempThingList.Add(inputList[i]);
}
}
private static void Threaded_CheckForbiddenAndAddToList(List<T> inputList, Pawn pawn)
{
_tempThingList.Clear();
#if DEBUG
DebugLog.Message("Running threading code");
var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
#endif
var partitioner = Partitioner.Create(0, inputList.Count);
Parallel.ForEach<Tuple<int, int>, (List<T> localList, List<T> sourceList, Pawn pawn)>(partitioner,
() => new(new(), inputList, pawn),
(range, state, vars) =>
{
for (int i = range.Item1; i < range.Item2; i++)
{
var item = vars.sourceList[i];
if (!item.IsForbidden(vars.pawn))
vars.localList.Add(item);
}
return vars;
},
vars =>
{
lock (_lock)
{
_tempThingList.AddRange(vars.localList);
}
});
#if DEBUG
stopwatch.Stop();
DebugLog.Message($"Finished threading code without errors in {stopwatch.Elapsed}");
#endif
}
public static List<T> Threaded_CheckForbiddenAndAddToAnyCollection(ICollection<T> inputCollection, Pawn pawn)
{
_tempThingList.Clear();
#if DEBUG
DebugLog.Message("Running threading code");
var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
#endif
Parallel.ForEach<T, (List<T> localList, Pawn pawn)>(inputCollection,
() => new(new(), pawn),
(thing, state, vars) =>
{
if (!thing.IsForbidden(vars.pawn))
vars.localList.Add(thing);
return vars;
},
vars =>
{
lock (_lock)
{
_tempThingList.AddRange(vars.localList);
}
});
#if DEBUG
stopwatch.Stop();
DebugLog.Message($"Finished threading code without errors in {stopwatch.Elapsed}");
#endif
return _tempThingList;
}
private static object _lock { get; } = new();
private static List<T> _tempThingList { get; } = new();
private static readonly List<T> _tempThingList = [];
}
public static Dictionary<IEnumerable<Thing>, IEnumerable<Thing>> PrefilteredPotentialWorkThingsGlobal { get; } = new();
public static Dictionary<IEnumerable<Thing>, IEnumerable<Thing>> PrefilteredPotentialWorkThingsGlobal { get; }
= [];
}
public sealed class ThingRequest_IsUndefined_Patch : FishPatch
{
public override string Description => "Part of the work scanning optimization";
public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.IsUndefined));
public override MethodBase TargetMethodInfo
=> AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.IsUndefined));
[HarmonyPriority(Priority.Last)]
public static void Postfix(ThingRequest __instance, ref bool __result)
{
@@ -255,6 +214,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
__result = NewResult(__instance);
}
public static bool NewResult(ThingRequest __instance)
=> __instance.group == ThingRequestGroup.Undefined && !((FishCache)__instance.singleDef).IsSingleDef;
}
@@ -262,7 +222,10 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
public sealed class ThingRequest_CanBeFoundInRegion_Patch : FirstPriorityFishPatch
{
public override string Description => "Part of the work scanning optimization";
public override MethodBase TargetMethodInfo => AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.CanBeFoundInRegion));
public override MethodBase TargetMethodInfo
=> AccessTools.PropertyGetter(typeof(ThingRequest), nameof(ThingRequest.CanBeFoundInRegion));
public static bool Prefix(ThingRequest __instance, ref bool __result)
{
if (__instance.singleDef is not FishCache)
@@ -271,15 +234,20 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
__result = NewResult(__instance);
return false;
}
public static bool NewResult(ThingRequest __instance)
=> ((FishCache)__instance.singleDef).IsSingleDef
|| (__instance.group != ThingRequestGroup.Undefined && (__instance.group == ThingRequestGroup.Nothing || __instance.group.StoreInRegion()));
|| (__instance.group != ThingRequestGroup.Undefined
&& (__instance.group == ThingRequestGroup.Nothing || __instance.group.StoreInRegion()));
}
public sealed class ListerThings_ThingsMatching_Patch : FirstPriorityFishPatch
{
public override string Description => "Part of the work scanning optimization";
public override Expression<Action> TargetMethod => () => new ListerThings(default).ThingsMatching(default);
public override MethodBase? TargetMethodInfo { get; }
= AccessTools.DeclaredMethod(typeof(ListerThings), nameof(ListerThings.ThingsMatching));
public static bool Prefix(ListerThings __instance, ThingRequest req, ref List<Thing> __result)
{
if (req.singleDef is not FishCache)
@@ -288,6 +256,7 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
__result = GetCache(__instance, req);
return false;
}
public static List<Thing> GetCache(ListerThings __instance, ThingRequest req)
{
var cachedThings = ((FishCache)req.singleDef).FilteredThings ?? ListerThings.EmptyList;
@@ -301,26 +270,35 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
return cachedThings;
}
}
public static List<Thing> GetForRegion(ListerThings __instance, ThingRequest req)
=> new(TryIssueJobPackage_Patch.Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(
(((FishCache)req.singleDef) is var cache && cache.IsSingleDef
? __instance.listsByDef.TryGetValue(cache.Def, out var value) ? value : null
: __instance.listsByGroup.TryGetItem((int)req.group, out var item) ? item : null) ?? ListerThings.EmptyList, CurrentPawn));
=>
[
..TryIssueJobPackage_Patch.Generic<Thing>.TryGetPrefilteredPotentialWorkThingsForList(
((FishCache)req.singleDef is { IsSingleDef: true } cache
? __instance.listsByDef.TryGetValue(cache.Def, out var value) ? value : null
: __instance.listsByGroup.TryGetItem((int)req.group, out var item)
? item
: null)
?? ListerThings.EmptyList, CurrentPawn)
];
}
public class FishCache : ThingDef
{
public bool IsSingleDef => Def != null;
public ThingDef Def { get; private set; }
public List<Thing> FilteredThings { get; set; }
public ThingDef? Def { get; private init; }
public List<Thing>? FilteredThings { get; set; }
public static FishCache ForDef(ThingDef def)
{
if (_dictOfDefs.TryGetValue(def, out var value))
return value;
_dictOfDefs[def] = value = new(def.defName) { Def = def };
return value;
}
public static FishCache ForGroup(ThingRequestGroup group)
{
if (_dictOfGroups.TryGetValue(group, out var value))
@@ -329,15 +307,17 @@ public sealed class JobGiver_WorkPatches : ClassWithFishPatches
_dictOfGroups[group] = value = new(group.ToString());
return value;
}
public static void ClearAll()
{
_dictOfGroups.Clear();
_dictOfDefs.Clear();
}
private FishCache(string name) => defName = name;
private static Dictionary<ThingRequestGroup, FishCache> _dictOfGroups { get; } = new();
private static Dictionary<ThingDef, FishCache> _dictOfDefs { get; } = new();
private static readonly Dictionary<ThingRequestGroup, FishCache> _dictOfGroups = new();
private static readonly Dictionary<ThingDef, FishCache> _dictOfDefs = new();
}
private static Pawn CurrentPawn { get; set; }
private static Pawn? CurrentPawn { get; set; }
}*/
@@ -66,11 +66,6 @@ public sealed class WorkGiver_DoBillPrepatches : ClassWithFishPrepatches
public override void Transpiler(ILProcessor ilProcessor, ModuleDefinition module)
=> ilProcessor.ReplaceBodyWith(ReplacementBody);
// public static CodeInstructions Transpiler(CodeInstructions codeInstructions, MethodBase method)
// => Reflection.MakeReplacementCall(AccessTools.Method(
// typeof(TryFindBestIngredientsHelper_InnerDelegate_Patch), nameof(ReplacementBody),
// generics: new[] { DelegateInstanceType }));
public static bool ReplacementBody(object __instance, Region r)
{
var instance = _delegateInstance.Get(__instance);
@@ -86,8 +81,11 @@ public sealed class WorkGiver_DoBillPrepatches : ClassWithFishPrepatches
var bill = thingValidator.Bill;
if (HasNoFilters(bill) // apparently applies to a rim reborn benches?
|| bill.billStack?.billGiver
#if V1_4
|| bill.billStack?.billGiver is Building_MechGestator
is Building_MechGestator
#else
is Building_WorkTableAutonomous
#endif
|| (BlackListedModExtensions.Count > 0
&& IsBlackListed(bill))) // <-- Fuck these too
@@ -532,9 +530,12 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
public static void Prefix(ref bool alreadySorted, Bill? bill)
=> alreadySorted = alreadySorted
|| (bill?.billStack is { } stack
#if V1_4
// ReSharper disable once MergeIntoPattern
&& stack.billGiver is not Building_MechGestator
&& stack.billGiver is not
#if V1_4
Building_MechGestator
#else
Building_WorkTableAutonomous
#endif
);
@@ -575,8 +576,11 @@ public sealed class WorkGiver_DoBillOptimization : ClassWithFishPatches
public static void MarkIngredientCountAsFound(IngredientCount ingredientCount, Bill? bill)
{
if (bill?.billStack is not { } billStack
|| billStack.billGiver is
#if V1_4
|| billStack.billGiver is Building_MechGestator
Building_MechGestator
#else
Building_WorkTableAutonomous
#endif
)
{