// 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 disabledForNow using System.Threading.Tasks; using Verse.AI; namespace PerformanceFish.JobSystem; public sealed class GenClosestPatches : ClassWithFishPatches { public sealed class ClosestThing_Global_Patch : FishPatch { 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 Expression 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 IntVec3 center; public float maxDistanceSquared; public Func? priorityGetter; public float closestDistSquared; public Predicate? validator; } public static Thing? ClosestThing_Global_Replacement(IntVec3 center, IEnumerable? searchSet, float maxDistance = 99999f, Predicate? validator = null, Func? 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 thingList) { Log.Message($"Running threading code for {validator.Target.GetType()}"); Exception? exception = null; try { Parallel.For<(IList 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 thingList: { for (var i = 0; i < thingList.Count; i++) Process(thingList[i], variables); break; } case IList pawnList: { for (var j = 0; j < pawnList.Count; j++) Process(pawnList[j], variables); break; } case IList buildingList: { for (var k = 0; k < buildingList.Count; k++) Process(buildingList[k], variables); break; } case IList 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, 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, 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 : JobGiver_Work_PredicateAccess { public override WorkGiver_Scanner? GetScanner(object? obj) => obj is T t ? Scanner(t) : null; public static AccessTools.FieldRef Scanner { get; } = AccessTools.FieldRefAccess("scanner"); } private abstract class GenClosest_PredicateAccess { public abstract Predicate? GetValidator(object? obj); } private class GenClosest_PredicateAccessImplementation : GenClosest_PredicateAccess { public override Predicate? GetValidator(object? obj) => obj is T t ? Validator(t) : null; public static AccessTools.FieldRef> Validator { get; } = AccessTools.FieldRefAccess>("validator"); } } #endif //}