Decompiled source of PathfindingLagFix Beta v2.0.8
PathfindingLagFix.dll
Decompiled 5 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using PathfindingLagFix.Patches; using PathfindingLagFix.Utilities; using PathfindingLagFix.Utilities.IL; using PathfindingLib.API; using PathfindingLib.Jobs; using PathfindingLib.Utilities; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; using Unity.Mathematics; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.Experimental.AI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("PathfindingLagFix")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("PathfindingLagFix")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("fedb984b-16ae-458c-b2cc-19590627c578")] [assembly: AssemblyFileVersion("2.0.8")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.0.8.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PathfindingLagFix { [BepInPlugin("Zaggy1024.PathfindingLagFix", "PathfindingLagFix", "2.0.8")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string MOD_NAME = "PathfindingLagFix"; public const string MOD_UNIQUE_NAME = "Zaggy1024.PathfindingLagFix"; public const string MOD_VERSION = "2.0.8"; private readonly Harmony harmony = new Harmony("Zaggy1024.PathfindingLagFix"); public static Plugin Instance { get; private set; } public ManualLogSource Logger => ((BaseUnityPlugin)this).Logger; public void Awake() { Instance = this; harmony.PatchAll(typeof(PatchEnemyAI)); harmony.PatchAll(typeof(PatchFlowermanAI)); harmony.PatchAll(typeof(PatchCentipedeAI)); harmony.PatchAll(typeof(PatchPufferAI)); harmony.PatchAll(typeof(PatchDoublewingAI)); harmony.PatchAll(typeof(PatchFlowerSnakeEnemy)); } } } namespace PathfindingLagFix.Utilities { internal static class AsyncDistancePathfinding { internal class EnemyDistancePathfindingStatus { public Coroutine Coroutine; public int CurrentSearchTypeID = -1; public GameObject[] AINodes; public Transform[] SortedNodes; public Vector3[] SortedPositions; public FindPathsToNodesJob Job; public JobHandle JobHandle; public Transform ChosenNode; public float MostOptimalDistance = float.PositiveInfinity; public void SortNodes(EnemyAI enemy, Vector3 target, bool furthestFirst) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) int num = enemy.allAINodes.Length; if (enemy.allAINodes != AINodes) { AINodes = enemy.allAINodes; SortedNodes = (Transform[])(object)new Transform[num]; SortedPositions = (Vector3[])(object)new Vector3[num]; for (int i = 0; i < num; i++) { Transform transform = AINodes[i].transform; SortedNodes[i] = transform; SortedPositions[i] = transform.position; } } for (int j = 1; j < num; j++) { Vector3 val = SortedPositions[j] - target; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; for (int num2 = j; num2 > 0; num2--) { val = SortedPositions[num2 - 1] - target; if ((((Vector3)(ref val)).sqrMagnitude <= sqrMagnitude) ^ furthestFirst) { break; } Swap<Transform>(ref SortedNodes[num2 - 1], ref SortedNodes[num2]); Swap<Vector3>(ref SortedPositions[num2 - 1], ref SortedPositions[num2]); } } static void Swap<T>(ref T a, ref T b) { T val2 = b; T val3 = a; a = val2; b = val3; } } public Transform RetrieveChosenNode(out float mostOptimalDistance) { Transform chosenNode = ChosenNode; mostOptimalDistance = MostOptimalDistance; if ((Object)(object)chosenNode == (Object)null) { return null; } ChosenNode = null; MostOptimalDistance = float.PositiveInfinity; CurrentSearchTypeID = -1; return chosenNode; } ~EnemyDistancePathfindingStatus() { Job.FreeAllResources(); } } internal delegate IEnumerator NodeSelectionCoroutine(EnemyDistancePathfindingStatus status); internal const float DEFAULT_CAP_DISTANCE = 60f; private static readonly IDMap<EnemyDistancePathfindingStatus> Statuses = new IDMap<EnemyDistancePathfindingStatus>(() => new EnemyDistancePathfindingStatus(), 1); internal static void RemoveStatus(EnemyAI enemy) { Statuses[enemy.thisEnemyIndex] = new EnemyDistancePathfindingStatus(); } internal static EnemyDistancePathfindingStatus StartJobs(EnemyAI enemy, EnemyDistancePathfindingStatus status, Vector3 target, int count, bool farthestFirst) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) NavMeshAgent agent = enemy.agent; Vector3 pathOrigin = AgentExtensions.GetPathOrigin(agent); status.SortNodes(enemy, target, farthestFirst); ref FindPathsToNodesJob job = ref status.Job; job.Initialize(agent.agentTypeID, agent.areaMask, pathOrigin, status.SortedPositions); status.JobHandle = IJobForExtensions.ScheduleByRef<FindPathsToNodesJob>(ref job, count, default(JobHandle)); return status; } internal static EnemyDistancePathfindingStatus StartChoosingNode(EnemyAI enemy, int searchTypeID, NodeSelectionCoroutine coroutine) { EnemyDistancePathfindingStatus enemyDistancePathfindingStatus = Statuses[enemy.thisEnemyIndex]; if (enemyDistancePathfindingStatus.CurrentSearchTypeID == searchTypeID) { return enemyDistancePathfindingStatus; } if (enemyDistancePathfindingStatus.Coroutine != null) { enemyDistancePathfindingStatus.Job.Cancel(); return enemyDistancePathfindingStatus; } enemyDistancePathfindingStatus.CurrentSearchTypeID = searchTypeID; enemyDistancePathfindingStatus.Coroutine = ((MonoBehaviour)enemy).StartCoroutine(coroutine(enemyDistancePathfindingStatus)); return enemyDistancePathfindingStatus; } private static EnemyDistancePathfindingStatus StartChoosingNode(EnemyAI enemy, int searchTypeID, Vector3 target, bool farthestFirst, bool avoidLineOfSight, int offset, float capDistance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) return StartChoosingNode(enemy, searchTypeID, (EnemyDistancePathfindingStatus status) => ChooseNodeCoroutine(enemy, status, target, farthestFirst, avoidLineOfSight, offset, capDistance)); } internal static EnemyDistancePathfindingStatus StartChoosingFarthestNodeFromPosition(EnemyAI enemy, int searchTypeID, Vector3 target, bool avoidLineOfSight = false, int offset = 0, float capDistance = 0f) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return StartChoosingNode(enemy, searchTypeID, target, farthestFirst: true, avoidLineOfSight, offset, capDistance); } internal static EnemyDistancePathfindingStatus StartChoosingClosestNodeToPosition(EnemyAI enemy, int searchTypeID, Vector3 target, bool avoidLineOfSight = false, int offset = 0, float capDistance = 0f) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return StartChoosingNode(enemy, searchTypeID, target, farthestFirst: false, avoidLineOfSight, offset, capDistance); } internal static IEnumerator ChooseNodeCoroutine(EnemyAI enemy, EnemyDistancePathfindingStatus status, Vector3 target, bool farthestFirst, bool avoidLineOfSight, int offset, float capDistance) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (enemy.allAINodes.Length == 0 || !enemy.agent.isOnNavMesh) { status.ChosenNode = ((Component)enemy).transform; status.MostOptimalDistance = 0f; yield break; } int candidateCount = enemy.allAINodes.Length; StartJobs(enemy, status, target, candidateCount, farthestFirst); FindPathsToNodesJob job = status.Job; JobHandle jobHandle = status.JobHandle; int result = -1; float capDistanceSqr = capDistance * capDistance; while (result == -1) { yield return null; bool flag = true; int num = Math.Min(offset, candidateCount - 1); Vector3 position = ((Component)enemy).transform.position; for (int i = 0; i < candidateCount; i++) { if (capDistanceSqr > 0f) { Vector3 val = status.SortedPositions[i] - position; if (((Vector3)(ref val)).sqrMagnitude > capDistanceSqr) { break; } } PathQueryStatus val2 = job.Statuses[i]; if ((int)PathQueryStatusExtensions.GetResult(val2) == 536870912) { flag = false; break; } if ((int)PathQueryStatusExtensions.GetResult(val2) == 1073741824) { NativeArray<NavMeshLocation> path = job.GetPath(i); NavMeshLocation val3 = path[0]; PolygonId polygon = ((NavMeshLocation)(ref val3)).polygon; if (((PolygonId)(ref polygon)).IsNull()) { Plugin.Instance.Logger.LogWarning((object)$"{i}: Path is null"); } else if ((!avoidLineOfSight || !LineOfSight.PathIsBlockedByLineOfSight(path)) && num-- == 0) { result = i; break; } } } if (!flag || result != -1) { continue; } for (int j = 0; j < candidateCount; j++) { if ((int)PathQueryStatusExtensions.GetResult(job.Statuses[j]) == 1073741824) { result = j; break; } } break; } job.Cancel(); if (result == -1 && status.SortedNodes.Length != 0) { result = 0; } if (result >= 0) { status.ChosenNode = status.SortedNodes[result]; status.MostOptimalDistance = Vector3.Distance(target, status.SortedPositions[result]); } while (!((JobHandle)(ref jobHandle)).IsCompleted) { yield return null; } status.Coroutine = null; } } internal static class AsyncPlayerPathfinding { internal class EnemyToPlayerPathfindingStatus { internal FindPathsToNodesJob PathsToPlayersJob; internal JobHandle PathsToPlayersJobHandle; internal bool hasStarted; private int[] playerJobIndices = Array.Empty<int>(); private readonly List<Vector3> validPlayerPositions = new List<Vector3>(); private bool lastRetrievedStatusWasInProgress; internal void StartJobs(EnemyAI enemy) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) hasStarted = true; NavMeshAgent agent = enemy.agent; Vector3 pathOrigin = AgentExtensions.GetPathOrigin(enemy.agent); PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (playerJobIndices.Length != allPlayerScripts.Length) { playerJobIndices = new int[allPlayerScripts.Length]; } validPlayerPositions.Clear(); lastRetrievedStatusWasInProgress = false; int num = 0; for (int i = 0; i < allPlayerScripts.Length; i++) { PlayerControllerB val = allPlayerScripts[i]; if (!enemy.PlayerIsTargetable(val, false, false)) { playerJobIndices[i] = -1; continue; } playerJobIndices[i] = num++; validPlayerPositions.Add(((Component)val).transform.position); } PathsToPlayersJob.Initialize(agent.agentTypeID, agent.areaMask, pathOrigin, validPlayerPositions); PathsToPlayersJobHandle = IJobForExtensions.ScheduleByRef<FindPathsToNodesJob>(ref PathsToPlayersJob, validPlayerPositions.Count, default(JobHandle)); } internal bool IsPathValid(int playerIndex) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 if (!hasStarted) { return false; } if (playerIndex >= playerJobIndices.Length) { return false; } int num = playerJobIndices[playerIndex]; if (num < 0) { return false; } PathQueryStatus result = PathQueryStatusExtensions.GetResult(PathsToPlayersJob.Statuses[num]); if ((int)result == 536870912) { lastRetrievedStatusWasInProgress = true; return false; } return (int)result == 1073741824; } internal void ResetIfResultsHaveBeenUsed() { if (hasStarted && (!lastRetrievedStatusWasInProgress || validPlayerPositions.Count <= 0)) { hasStarted = false; } } ~EnemyToPlayerPathfindingStatus() { PathsToPlayersJob.FreeAllResources(); } } private static readonly IDMap<EnemyToPlayerPathfindingStatus> Statuses = new IDMap<EnemyToPlayerPathfindingStatus>(() => new EnemyToPlayerPathfindingStatus(), 1); internal static void RemoveStatus(EnemyAI enemy) { Statuses[enemy.thisEnemyIndex] = new EnemyToPlayerPathfindingStatus(); } internal static EnemyToPlayerPathfindingStatus GetStatus(EnemyAI enemy) { return Statuses[enemy.thisEnemyIndex]; } } internal static class AsyncRoamingPathfinding { internal class EnemyRoamingPathfindingStatus { internal FindPathsToNodesJob PathsFromEnemyJob; internal JobHandle PathsFromEnemyJobHandle; internal FindPathsToNodesJob PathsFromSearchStartJob; internal JobHandle PathsFromSearchStartJobHandle; private int nodeCount; internal void StartJobs(EnemyAI enemy) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) AISearchRoutine currentSearch = enemy.currentSearch; NavMeshAgent agent = enemy.agent; Vector3 pathOrigin = AgentExtensions.GetPathOrigin(enemy.agent); List<GameObject> unsearchedNodes = currentSearch.unsearchedNodes; nodeCount = unsearchedNodes.Count; Vector3[] array = (Vector3[])(object)new Vector3[nodeCount]; for (int i = 0; i < unsearchedNodes.Count; i++) { array[GetJobIndex(i)] = unsearchedNodes[i].transform.position; } PathsFromEnemyJob.Initialize(agent.agentTypeID, agent.areaMask, pathOrigin, array, currentSearch.startedSearchAtSelf); PathsFromEnemyJobHandle = IJobForExtensions.ScheduleByRef<FindPathsToNodesJob>(ref PathsFromEnemyJob, unsearchedNodes.Count, default(JobHandle)); if (!currentSearch.startedSearchAtSelf) { PathsFromSearchStartJob.Initialize(agent.agentTypeID, agent.areaMask, currentSearch.currentSearchStartPosition, array, calculateDistance: true); PathsFromSearchStartJobHandle = IJobForExtensions.ScheduleByRef<FindPathsToNodesJob>(ref PathsFromSearchStartJob, unsearchedNodes.Count, default(JobHandle)); } } internal int GetJobIndex(int index) { return nodeCount - 1 - index; } ~EnemyRoamingPathfindingStatus() { PathsFromEnemyJob.FreeAllResources(); PathsFromSearchStartJob.FreeAllResources(); } } private static readonly IDMap<EnemyRoamingPathfindingStatus> Statuses = new IDMap<EnemyRoamingPathfindingStatus>(() => new EnemyRoamingPathfindingStatus(), 1); internal static void RemoveStatus(EnemyAI enemy) { Statuses[enemy.thisEnemyIndex] = new EnemyRoamingPathfindingStatus(); } internal static EnemyRoamingPathfindingStatus GetStatus(EnemyAI enemy) { return Statuses[enemy.thisEnemyIndex]; } } internal struct FindPathsToNodesJob : IJobFor { [NativeDisableContainerSafetyRestriction] private static NativeArray<NavMeshQuery> StaticThreadQueries; private const float MAX_ORIGIN_DISTANCE = 5f; private const float MAX_ENDPOINT_DISTANCE = 1.5f; private const float MAX_ENDPOINT_DISTANCE_SQR = 2.25f; [ReadOnly] [NativeSetThreadIndex] internal int ThreadIndex; [ReadOnly] [NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] internal NativeArray<NavMeshQuery> ThreadQueriesRef; [ReadOnly] private int AgentTypeID; [ReadOnly] private int AreaMask; [ReadOnly] private Vector3 Origin; [ReadOnly] [NativeDisableContainerSafetyRestriction] private NativeArray<Vector3> Destinations; [ReadOnly] private bool CalculateDistance; [ReadOnly] [NativeDisableContainerSafetyRestriction] private NativeArray<bool> Canceled; [WriteOnly] [NativeDisableContainerSafetyRestriction] internal NativeArray<PathQueryStatus> Statuses; [WriteOnly] [NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] internal NativeArray<NavMeshLocation> Paths; [WriteOnly] [NativeDisableContainerSafetyRestriction] internal NativeArray<int> PathSizes; [WriteOnly] [NativeDisableContainerSafetyRestriction] internal NativeArray<float> PathDistances; public unsafe void Initialize(int agentTypeID, int areaMask, Vector3 origin, Vector3[] candidates, int count, bool calculateDistance = false) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) ThreadQueriesRef = Unsafe.Read<NativeArray<NavMeshQuery>>((void*)PathfindingJobSharedResources.GetPerThreadQueriesArray()); CreateFixedArrays(); AgentTypeID = agentTypeID; AreaMask = areaMask; Origin = origin; CalculateDistance = calculateDistance; EnsureCount(count); for (int i = 0; i < count; i++) { Statuses[i] = (PathQueryStatus)536870912; } if (calculateDistance) { for (int j = 0; j < count; j++) { PathDistances[j] = 0f; } } Canceled[0] = false; NativeArray<Vector3>.Copy(candidates, Destinations, count); } public void Initialize(int agentTypeID, int areaMask, Vector3 origin, Vector3[] candidates, bool calculateDistance = false) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) Initialize(agentTypeID, areaMask, origin, candidates, candidates.Length, calculateDistance); } public void Initialize(int agentTypeID, int areaMask, Vector3 origin, List<Vector3> candidates, bool calculateDistance = false) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) Initialize(agentTypeID, areaMask, origin, NoAllocHelpers.ExtractArrayFromListT<Vector3>(candidates), candidates.Count, calculateDistance); } private void CreateFixedArrays() { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Canceled = new NativeArray<bool>(1, (Allocator)4, (NativeArrayOptions)1); } private void DisposeFixedArrays() { Canceled.Dispose(); } private void EnsureCount(int count) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (Destinations.Length < count) { DisposeResizeableArrays(); if (count != 0) { Destinations = new NativeArray<Vector3>(count, (Allocator)4, (NativeArrayOptions)1); Statuses = new NativeArray<PathQueryStatus>(count, (Allocator)4, (NativeArrayOptions)1); Paths = new NativeArray<NavMeshLocation>(count * 128, (Allocator)4, (NativeArrayOptions)1); PathSizes = new NativeArray<int>(count, (Allocator)4, (NativeArrayOptions)1); PathDistances = new NativeArray<float>(count, (Allocator)4, (NativeArrayOptions)1); } } } private void DisposeResizeableArrays() { if (Destinations.IsCreated) { Destinations.Dispose(); Statuses.Dispose(); Paths.Dispose(); PathSizes.Dispose(); PathDistances.Dispose(); } } public void Cancel() { if (Canceled.IsCreated) { Canceled[0] = true; } } public NativeArray<NavMeshLocation> GetPathBuffer(int index) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return Paths.GetSubArray(index * 128, 128); } public NativeArray<NavMeshLocation> GetPath(int index) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) return Paths.GetSubArray(index * 128, PathSizes[index]); } public void Execute(int index) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Invalid comparison between Unknown and I4 //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Invalid comparison between Unknown and I4 //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Invalid comparison between Unknown and I4 //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Invalid comparison between Unknown and I4 //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) if (Canceled[0]) { Statuses[index] = (PathQueryStatus)int.MinValue; return; } NavMeshLock.BeginRead(); NavMeshQuery val = ThreadQueriesRef[ThreadIndex]; Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(5f, 5f, 5f); NavMeshLocation val3 = ((NavMeshQuery)(ref val)).MapLocation(Origin, val2, AgentTypeID, AreaMask); if (!((NavMeshQuery)(ref val)).IsValid(val3)) { Statuses[index] = (PathQueryStatus)int.MinValue; NavMeshLock.EndRead(); return; } Vector3 val4 = Destinations[index]; Vector3 val5 = default(Vector3); ((Vector3)(ref val5))..ctor(1.5f, 1.5f, 1.5f); NavMeshLocation val6 = ((NavMeshQuery)(ref val)).MapLocation(val4, val5, AgentTypeID, AreaMask); if (!((NavMeshQuery)(ref val)).IsValid(val6)) { Statuses[index] = (PathQueryStatus)int.MinValue; NavMeshLock.EndRead(); return; } PathQueryStatus val7 = ((NavMeshQuery)(ref val)).BeginFindPath(val3, val6, AreaMask, default(NativeArray<float>)); if ((int)PathQueryStatusExtensions.GetResult(val7) == int.MinValue) { Statuses[index] = val7; NavMeshLock.EndRead(); return; } int num = default(int); while ((int)PathQueryStatusExtensions.GetResult(val7) == 536870912) { val7 = ((NavMeshQuery)(ref val)).UpdateFindPath(NavMeshLock.RecommendedUpdateFindPathIterationCount, ref num); NavMeshLock.YieldRead(); } int num2 = default(int); val7 = ((NavMeshQuery)(ref val)).EndFindPath(ref num2); if ((int)PathQueryStatusExtensions.GetResult(val7) != 1073741824) { Statuses[index] = val7; NavMeshLock.EndRead(); return; } NativeArray<PolygonId> val8 = default(NativeArray<PolygonId>); val8..ctor(num2, (Allocator)2, (NativeArrayOptions)1); ((NavMeshQuery)(ref val)).GetPathResult(NativeSlice<PolygonId>.op_Implicit(val8)); int num3 = default(int); val7 = (PathQueryStatus)(NavMeshQueryUtils.FindStraightPath(val, float3.op_Implicit(Origin), float3.op_Implicit(val4), NativeSlice<PolygonId>.op_Implicit(val8), num2, GetPathBuffer(index), ref num3) | PathQueryStatusExtensions.GetDetail(val7)); NavMeshLock.EndRead(); PathSizes[index] = num3; val8.Dispose(); if ((int)PathQueryStatusExtensions.GetResult(val7) != 1073741824) { Statuses[index] = val7; return; } NativeArray<NavMeshLocation> path = GetPath(index); NavMeshLocation val9 = path[path.Length - 1]; Vector3 val10 = ((NavMeshLocation)(ref val9)).position - val4; if (((Vector3)(ref val10)).sqrMagnitude > 2.25f) { Statuses[index] = (PathQueryStatus)(int.MinValue | PathQueryStatusExtensions.GetDetail(val7)); return; } if (CalculateDistance) { float num4 = 0f; for (int i = 1; i < path.Length; i++) { float num5 = num4; val9 = path[i - 1]; Vector3 position = ((NavMeshLocation)(ref val9)).position; val9 = path[i]; num4 = num5 + Vector3.Distance(position, ((NavMeshLocation)(ref val9)).position); } PathDistances[index] = num4; } Statuses[index] = val7; } internal void FreeAllResources() { DisposeResizeableArrays(); DisposeFixedArrays(); } } public class IDMap<T> : IEnumerable<T>, IEnumerable { private readonly Func<T> constructor; private T[] backingArray; public ref T this[int index] => ref GetItem(index); public int Count => backingArray.Length; public IDMap(Func<T> elementConstructor, int capacity) { constructor = elementConstructor; backingArray = new T[capacity]; for (int i = 0; i < capacity; i++) { backingArray[i] = constructor(); } } private void EnsureCapacity(int capacity) { if (backingArray.Length < capacity) { T[] array = new T[capacity]; Array.Copy(backingArray, array, Math.Min(backingArray.Length, array.Length)); for (int i = backingArray.Length; i < capacity; i++) { array[i] = constructor(); } backingArray = array; } } public ref T GetItem(int index) { EnsureCapacity(index + 1); return ref backingArray[index]; } public int IndexOf(T item) { return Array.IndexOf(backingArray, item); } public void Clear() { backingArray = Array.Empty<T>(); } public bool Clear(T item) { int num = IndexOf(item); if (num == -1) { return false; } this[num] = default(T); return true; } public bool Contains(T item) { return Array.IndexOf(backingArray, item) != -1; } public void CopyTo(T[] array, int arrayIndex) { Array.Copy(backingArray, 0, array, arrayIndex, backingArray.Length); } public void CopyTo(int sourceIndex, T[] array, int arrayIndex, int count) { Array.Copy(backingArray, sourceIndex, array, arrayIndex, count); } public IEnumerator<T> GetEnumerator() { return ((IEnumerable<T>)backingArray).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return backingArray.GetEnumerator(); } } internal static class LineOfSight { private const int LINE_OF_SIGHT_LAYER_MASK = 262144; public static bool PathIsBlockedByLineOfSight(NativeArray<NavMeshLocation> path, Vector3? checkLOSToPosition = null) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (path.Length <= 1) { return false; } bool flag = false; NavMeshLocation val = path[0]; Vector3 val2 = ((NavMeshLocation)(ref val)).position; for (int i = 1; i < path.Length && i < 16; i++) { if (flag) { break; } val = path[i]; Vector3 position = ((NavMeshLocation)(ref val)).position; if (checkLOSToPosition.HasValue) { Vector3 val3 = (val2 + position) * 0.5f; Vector3 val4 = Vector3.up * 0.25f; if (!Physics.Linecast(val3 + val4, checkLOSToPosition.Value + val4, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { return true; } } if (Physics.Linecast(val2, position, 262144)) { return true; } val2 = position; } return false; } } } namespace PathfindingLagFix.Utilities.IL { public class ILInjector { private const string INVALID = "Injector is invalid"; private const string MATCH_END_INVALID = "The end of the last search was invalid"; private List<CodeInstruction> instructions = instructions.ToList(); private ILGenerator generator; private int index; private int matchEnd; public bool IsValid { get { if (instructions != null) { return IsIndexValid(index); } return false; } } public CodeInstruction Instruction { get { if (!IsIndexInRange(index)) { return null; } return instructions[index]; } set { if (!IsIndexInRange(index)) { throw new InvalidOperationException($"Current index {index} is out of range of instruction count {instructions.Count}"); } instructions[index] = value; } } public CodeInstruction LastMatchedInstruction { get { int num = matchEnd - 1; if (!IsIndexInRange(num)) { return null; } return instructions[num]; } set { int num = matchEnd - 1; if (!IsIndexInRange(num)) { throw new InvalidOperationException($"Last matched index {index} is out of range of instruction count {instructions.Count}"); } instructions[num] = value; } } public ICollection<CodeInstruction> Instructions => instructions.AsReadOnly(); public ILInjector(IEnumerable<CodeInstruction> instructions, ILGenerator generator = null) { this.generator = generator; matchEnd = -1; base..ctor(); } public ILInjector GoToStart() { matchEnd = index; index = 0; return this; } public ILInjector GoToEnd() { matchEnd = index; index = instructions.Count; return this; } public ILInjector Forward(int offset) { if (!IsValid) { return this; } matchEnd = index; index = Math.Clamp(index + offset, -1, instructions.Count); return this; } public ILInjector Back(int offset) { return Forward(-offset); } private void MarkInvalid() { index = -1; matchEnd = -1; } private void Search(bool forward, ILMatcher[] predicates) { if (!IsValid) { return; } int num = 1; if (!forward) { num = -1; index--; } while (forward ? (index < instructions.Count) : (index >= 0)) { if (forward && index + predicates.Length > instructions.Count) { index = instructions.Count; break; } int i; for (i = 0; i < predicates.Length && predicates[i].Matches(instructions[index + i]); i++) { } if (i == predicates.Length) { matchEnd = index + i; return; } index += num; } MarkInvalid(); } public ILInjector Find(params ILMatcher[] predicates) { Search(forward: true, predicates); return this; } public ILInjector ReverseFind(params ILMatcher[] predicates) { Search(forward: false, predicates); return this; } public ILInjector GoToPush(int popIndex) { if (!IsValid) { return this; } matchEnd = index; index--; int num = 0; while (index >= 0) { CodeInstruction instruction = instructions[index]; num += instruction.PushCount(); num -= instruction.PopCount(); if (num > popIndex) { return this; } index--; } return this; } public ILInjector SkipBranch() { if (Instruction == null) { return this; } if (!(Instruction.operand is Label label)) { throw new InvalidOperationException($"Current instruction is not a branch: {Instruction}"); } return FindLabel(label); } public ILInjector FindLabel(Label label) { matchEnd = index + 1; for (index = 0; index < instructions.Count; index++) { if (instructions[index].labels.Contains(label)) { return this; } } MarkInvalid(); return this; } public ILInjector GoToMatchEnd() { index = matchEnd; return this; } public ILInjector GoToLastMatchedInstruction() { if (!IsIndexValid(matchEnd)) { return this; } index = matchEnd - 1; return this; } private bool IsIndexValid(int index) { return index != -1; } private bool IsIndexInRange(int index) { if (index >= 0) { return index < instructions.Count; } return false; } public CodeInstruction GetRelativeInstruction(int offset) { if (!IsValid) { throw new InvalidOperationException("Injector is invalid"); } int num = index + offset; if (!IsIndexInRange(num)) { throw new IndexOutOfRangeException($"Offset {offset} would read out of bounds at index {num}"); } return instructions[num]; } public void SetRelativeInstruction(int offset, CodeInstruction instruction) { if (!IsValid) { throw new InvalidOperationException("Injector is invalid"); } int num = index + offset; if (!IsIndexInRange(num)) { throw new IndexOutOfRangeException($"Offset {offset} would write out of bounds at index {num}"); } instructions[num] = instruction; } public IEnumerable<CodeInstruction> GetRelativeInstructions(int offset, int size) { for (int i = 0; i < size; i++) { yield return instructions[index + offset + i]; } } public IEnumerable<CodeInstruction> GetRelativeInstructions(int size) { return GetRelativeInstructions(0, size); } private void GetLastMatchRangeAbsolute(out int start, out int end) { start = index; end = matchEnd; if (start > end) { int num = end; int num2 = start; start = num; end = num2; } } private void GetLastMatchRange(out int start, out int size) { GetLastMatchRangeAbsolute(out start, out var end); if (start < 0 || start >= instructions.Count) { throw new InvalidOperationException($"Last match range starts at invalid index {start}"); } if (end < 0 || end > instructions.Count) { throw new InvalidOperationException($"Last match range ends at invalid index {end}"); } size = end - start; } public List<CodeInstruction> GetLastMatch() { GetLastMatchRange(out var start, out var size); return instructions.GetRange(start, size); } public Label AddLabel() { if (generator == null) { throw new InvalidOperationException("No ILGenerator was provided"); } Label label = generator.DefineLabel(); Instruction.labels.Add(label); return label; } public ILInjector AddLabel(Label label) { Instruction.labels.Add(label); return this; } public ILInjector InsertInPlace(params CodeInstruction[] instructions) { if (!IsValid) { throw new InvalidOperationException("Injector is invalid"); } this.instructions.InsertRange(index, instructions); if (matchEnd >= index) { matchEnd += instructions.Length; } return this; } public ILInjector Insert(params CodeInstruction[] instructions) { InsertInPlace(instructions); index += instructions.Length; return this; } public ILInjector InsertInPlaceAfterBranch(params CodeInstruction[] instructions) { if (!IsValid) { throw new InvalidOperationException("Injector is invalid"); } List<Label> labels = Instruction.labels; this.instructions.InsertRange(index, instructions); Instruction.labels.AddRange(labels); labels.Clear(); if (matchEnd >= index) { matchEnd += instructions.Length; } return this; } public ILInjector InsertAfterBranch(params CodeInstruction[] instructions) { InsertInPlaceAfterBranch(instructions); index += instructions.Length; return this; } public ILInjector RemoveAllPreviousInstructions() { if (!IsValid) { throw new InvalidOperationException("Injector is invalid"); } instructions.RemoveRange(0, index); matchEnd -= index; if (matchEnd < 0) { matchEnd = 0; } index = 0; return this; } public ILInjector Remove(int count = 1) { if (!IsValid) { throw new InvalidOperationException("Injector is invalid"); } instructions.RemoveRange(index, count); if (matchEnd > index) { matchEnd = Math.Max(index, matchEnd - count); } return this; } public ILInjector RemoveLastMatch() { GetLastMatchRange(out var start, out var size); instructions.RemoveRange(start, size); index = start; matchEnd = start; return this; } public ILInjector ReplaceLastMatch(params CodeInstruction[] instructions) { RemoveLastMatch(); Insert(instructions); return this; } public List<CodeInstruction> ReleaseInstructions() { List<CodeInstruction> result = instructions; instructions = null; return result; } public ILInjector PrintContext(int context, string header = "") { if (!IsValid) { throw new InvalidOperationException("Injector is invalid (" + header + ")"); } StringBuilder stringBuilder = new StringBuilder(header); if (header.Length > 0) { stringBuilder.Append(':'); } stringBuilder.AppendLine(); GetLastMatchRangeAbsolute(out var start, out var end); int num = Math.Min(end + 1 + context, instructions.Count); for (int i = Math.Max(start - context, 0); i < num; i++) { if (end == -1 && i == index) { stringBuilder.Append("╶> "); } else { if (i >= start && i < end) { stringBuilder.Append("│"); } else { stringBuilder.Append(" "); } if (i == index) { stringBuilder.Append("╶> "); } else { stringBuilder.Append(" "); } } stringBuilder.AppendLine($"{i}: {instructions[i]}"); } Plugin.Instance.Logger.LogInfo((object)stringBuilder); return this; } public ILInjector PrintContext(string header = "") { return PrintContext(4, header); } } public interface ILMatcher { private static OpCode[] branchInstructions = new OpCode[4] { OpCodes.Br, OpCodes.Br_S, OpCodes.Brfalse, OpCodes.Brfalse_S }; bool Matches(CodeInstruction instruction); static NotMatcher Not(ILMatcher matcher) { return new NotMatcher(matcher); } static OpcodeMatcher Opcode(OpCode opcode) { return new OpcodeMatcher(opcode); } static OpcodesMatcher Opcodes(params OpCode[] opcodes) { return new OpcodesMatcher(opcodes); } static OpcodeOperandMatcher OpcodeOperand(OpCode opcode, object operand) { return new OpcodeOperandMatcher(opcode, operand); } static InstructionMatcher Instruction(CodeInstruction instruction) { return new InstructionMatcher(instruction); } static LdargMatcher Ldarg(int? arg = null) { return new LdargMatcher(arg); } static LdlocMatcher Ldloc(int? loc = null) { return new LdlocMatcher(loc); } static StlocMatcher Stloc(int? loc = null) { return new StlocMatcher(loc); } static LdcI32Matcher Ldc(int? value = null) { return new LdcI32Matcher(value); } static BranchMatcher Branch() { return new BranchMatcher(); } static OpcodeOperandMatcher Ldfld(FieldInfo field) { if (field == null) { Plugin.Instance.Logger.LogWarning((object)$"Field passed to ILMatcher.Ldfld() was null\n{new StackTrace()}"); } return new OpcodeOperandMatcher(OpCodes.Ldfld, field); } static OpcodeOperandMatcher Ldsfld(FieldInfo field) { if (field == null) { Plugin.Instance.Logger.LogWarning((object)$"Field passed to ILMatcher.Ldsfld() was null\n{new StackTrace()}"); } return new OpcodeOperandMatcher(OpCodes.Ldsfld, field); } static OpcodeOperandMatcher Stfld(FieldInfo field) { if (field == null) { Plugin.Instance.Logger.LogWarning((object)$"Field passed to ILMatcher.Stfld() was null\n{new StackTrace()}"); } return new OpcodeOperandMatcher(OpCodes.Stfld, field); } static OpcodeOperandMatcher Stsfld(FieldInfo field) { if (field == null) { Plugin.Instance.Logger.LogWarning((object)$"Field passed to ILMatcher.Stsfld() was null\n{new StackTrace()}"); } return new OpcodeOperandMatcher(OpCodes.Stsfld, field); } static OpcodeOperandMatcher Callvirt(MethodBase method) { if (method == null) { Plugin.Instance.Logger.LogWarning((object)$"Method passed to ILMatcher.Callvirt() was null\n{new StackTrace()}"); } return OpcodeOperand(OpCodes.Callvirt, method); } static OpcodeOperandMatcher Call(MethodBase method) { if (method == null) { Plugin.Instance.Logger.LogWarning((object)$"Method passed to ILMatcher.Call() was null\n{new StackTrace()}"); } return OpcodeOperand(OpCodes.Call, method); } static PredicateMatcher Predicate(Func<CodeInstruction, bool> predicate) { return new PredicateMatcher(predicate); } static PredicateMatcher Predicate(Func<FieldInfo, bool> predicate) { return new PredicateMatcher((CodeInstruction insn) => insn.operand is FieldInfo arg && predicate(arg)); } } public class NotMatcher : ILMatcher { private readonly ILMatcher matcher; public NotMatcher(ILMatcher matcher) { this.matcher = matcher; base..ctor(); } public bool Matches(CodeInstruction instruction) { return !matcher.Matches(instruction); } } public class OpcodeMatcher : ILMatcher { private readonly OpCode opcode; public OpcodeMatcher(OpCode opcode) { this.opcode = opcode; base..ctor(); } public bool Matches(CodeInstruction instruction) { return instruction.opcode == opcode; } } public class OpcodesMatcher : ILMatcher { private readonly OpCode[] opcodes; public OpcodesMatcher(OpCode[] opcodes) { this.opcodes = opcodes; base..ctor(); } public bool Matches(CodeInstruction instruction) { return opcodes.Contains(instruction.opcode); } } public class OpcodeOperandMatcher : ILMatcher { private readonly OpCode opcode; private readonly object operand; public OpcodeOperandMatcher(OpCode opcode, object operand) { this.opcode = opcode; this.operand = operand; base..ctor(); } public bool Matches(CodeInstruction instruction) { if (instruction.opcode == opcode) { return instruction.operand == operand; } return false; } } public class InstructionMatcher : ILMatcher { private readonly OpCode opcode = instruction.opcode; private readonly object operand = instruction.operand; private readonly Label[] labels = instruction.labels.ToArray(); public InstructionMatcher(CodeInstruction instruction) { } public bool Matches(CodeInstruction instruction) { if (instruction.opcode != opcode) { return false; } if (instruction.operand != operand) { return false; } if (instruction.labels.Count != labels.Length) { return false; } for (int i = 0; i < labels.Length; i++) { if (labels[i] != instruction.labels[i]) { return false; } } return true; } } public class LdargMatcher : ILMatcher { private readonly int? arg; public LdargMatcher(int? arg) { this.arg = arg; base..ctor(); } public bool Matches(CodeInstruction instruction) { if (!arg.HasValue) { return instruction.GetLdargIndex().HasValue; } return instruction.GetLdargIndex() == arg; } } public class LdlocMatcher : ILMatcher { private readonly int? loc; public LdlocMatcher(int? loc) { this.loc = loc; base..ctor(); } public bool Matches(CodeInstruction instruction) { if (!loc.HasValue) { return instruction.GetLdlocIndex().HasValue; } return instruction.GetLdlocIndex() == loc; } } public class StlocMatcher : ILMatcher { private readonly int? loc; public StlocMatcher(int? loc) { this.loc = loc; base..ctor(); } public bool Matches(CodeInstruction instruction) { if (!loc.HasValue) { return instruction.GetStlocIndex().HasValue; } return instruction.GetStlocIndex() == loc; } } public class LdcI32Matcher : ILMatcher { private readonly int? value; public LdcI32Matcher(int? value) { this.value = value; base..ctor(); } public bool Matches(CodeInstruction instruction) { if (value.HasValue || !instruction.GetLdcI32().HasValue) { return instruction.GetLdcI32() == value; } return true; } } public class BranchMatcher : ILMatcher { public bool Matches(CodeInstruction instruction) { Label? label = default(Label?); return CodeInstructionExtensions.Branches(instruction, ref label); } } public class PredicateMatcher : ILMatcher { private readonly Func<CodeInstruction, bool> predicate; public PredicateMatcher(Func<CodeInstruction, bool> predicate) { this.predicate = predicate; base..ctor(); } public bool Matches(CodeInstruction instruction) { return predicate(instruction); } } internal static class InstructionUtilities { public static CodeInstruction MakeLdarg(int index) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown if (index < 256) { return (CodeInstruction)(index switch { 0 => (object)new CodeInstruction(OpCodes.Ldarg_0, (object)null), 1 => (object)new CodeInstruction(OpCodes.Ldarg_1, (object)null), 2 => (object)new CodeInstruction(OpCodes.Ldarg_2, (object)null), 3 => (object)new CodeInstruction(OpCodes.Ldarg_3, (object)null), _ => (object)new CodeInstruction(OpCodes.Ldarg_S, (object)index), }); } return new CodeInstruction(OpCodes.Ldarg, (object)index); } public static int PopCount(this CodeInstruction instruction) { if (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) { MethodInfo obj = (MethodInfo)instruction.operand; int num = obj.GetParameters().Length; if (!obj.IsStatic) { num++; } return num; } if (instruction.opcode == OpCodes.Ret) { return 1; } return instruction.opcode.StackBehaviourPop switch { StackBehaviour.Pop0 => 0, StackBehaviour.Pop1 => 1, StackBehaviour.Pop1_pop1 => 2, StackBehaviour.Popi => 1, StackBehaviour.Popi_pop1 => 2, StackBehaviour.Popi_popi => 2, StackBehaviour.Popi_popi8 => 2, StackBehaviour.Popi_popi_popi => 3, StackBehaviour.Popi_popr4 => 2, StackBehaviour.Popi_popr8 => 2, StackBehaviour.Popref => 1, StackBehaviour.Popref_pop1 => 2, StackBehaviour.Popref_popi => 2, StackBehaviour.Popref_popi_popi => 3, StackBehaviour.Popref_popi_popi8 => 3, StackBehaviour.Popref_popi_popr4 => 3, StackBehaviour.Popref_popi_popr8 => 3, StackBehaviour.Popref_popi_popref => 3, StackBehaviour.Varpop => throw new NotImplementedException($"Variable pop on non-call instruction '{instruction}'"), StackBehaviour.Popref_popi_pop1 => 3, _ => throw new NotSupportedException($"StackBehaviourPop of {instruction.opcode.StackBehaviourPop} was not a pop for instruction '{instruction}'"), }; } public static int PushCount(this CodeInstruction instruction) { if (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) { if (((MethodInfo)instruction.operand).ReturnType == typeof(void)) { return 0; } return 1; } return instruction.opcode.StackBehaviourPush switch { StackBehaviour.Push0 => 0, StackBehaviour.Push1 => 1, StackBehaviour.Push1_push1 => 2, StackBehaviour.Pushi => 1, StackBehaviour.Pushi8 => 1, StackBehaviour.Pushr4 => 1, StackBehaviour.Pushr8 => 1, StackBehaviour.Pushref => 1, StackBehaviour.Varpush => throw new NotImplementedException($"Variable push on non-call instruction '{instruction}'"), _ => throw new NotSupportedException($"StackBehaviourPush of {instruction.opcode.StackBehaviourPush} was not a push for instruction '{instruction}'"), }; } public static int? GetLdargIndex(this CodeInstruction instruction) { OpCode opcode = instruction.opcode; if (opcode == OpCodes.Ldarg_0) { return 0; } if (opcode == OpCodes.Ldarg_1) { return 1; } if (opcode == OpCodes.Ldarg_2) { return 2; } if (opcode == OpCodes.Ldarg_3) { return 3; } if (opcode == OpCodes.Ldarg || opcode == OpCodes.Ldarg_S) { return instruction.operand as int?; } return null; } public static int? GetLdlocIndex(this CodeInstruction instruction) { OpCode opcode = instruction.opcode; if (opcode == OpCodes.Ldloc_0) { return 0; } if (opcode == OpCodes.Ldloc_1) { return 1; } if (opcode == OpCodes.Ldloc_2) { return 2; } if (opcode == OpCodes.Ldloc_3) { return 3; } if (opcode == OpCodes.Ldloc || opcode == OpCodes.Ldloc_S) { return (instruction.operand as LocalBuilder)?.LocalIndex; } return null; } public static int? GetStlocIndex(this CodeInstruction instruction) { OpCode opcode = instruction.opcode; if (opcode == OpCodes.Stloc_0) { return 0; } if (opcode == OpCodes.Stloc_1) { return 1; } if (opcode == OpCodes.Stloc_2) { return 2; } if (opcode == OpCodes.Stloc_3) { return 3; } if (opcode == OpCodes.Stloc || opcode == OpCodes.Stloc_S) { return (instruction.operand as LocalBuilder)?.LocalIndex; } return null; } public static CodeInstruction LdlocToStloc(this CodeInstruction instruction) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown OpCode opcode = instruction.opcode; if (opcode == OpCodes.Ldloc_0) { return new CodeInstruction(OpCodes.Stloc_0, (object)null); } if (opcode == OpCodes.Ldloc_1) { return new CodeInstruction(OpCodes.Stloc_1, (object)null); } if (opcode == OpCodes.Ldloc_2) { return new CodeInstruction(OpCodes.Stloc_2, (object)null); } if (opcode == OpCodes.Ldloc_3) { return new CodeInstruction(OpCodes.Stloc_3, (object)null); } if (opcode == OpCodes.Ldloc || opcode == OpCodes.Ldloc_S) { return new CodeInstruction(OpCodes.Stloc, instruction.operand); } return null; } public static CodeInstruction StlocToLdloc(this CodeInstruction instruction) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown OpCode opcode = instruction.opcode; if (opcode == OpCodes.Stloc_0) { return new CodeInstruction(OpCodes.Ldloc_0, (object)null); } if (opcode == OpCodes.Stloc_1) { return new CodeInstruction(OpCodes.Ldloc_1, (object)null); } if (opcode == OpCodes.Stloc_2) { return new CodeInstruction(OpCodes.Ldloc_2, (object)null); } if (opcode == OpCodes.Stloc_3) { return new CodeInstruction(OpCodes.Ldloc_3, (object)null); } if (opcode == OpCodes.Stloc || opcode == OpCodes.Stloc_S) { return new CodeInstruction(OpCodes.Ldloc, instruction.operand); } return null; } public static int? GetLdcI32(this CodeInstruction instruction) { OpCode opcode = instruction.opcode; if (opcode == OpCodes.Ldc_I4_M1) { return -1; } if (opcode == OpCodes.Ldc_I4_0) { return 0; } if (opcode == OpCodes.Ldc_I4_1) { return 1; } if (opcode == OpCodes.Ldc_I4_2) { return 2; } if (opcode == OpCodes.Ldc_I4_3) { return 3; } if (opcode == OpCodes.Ldc_I4_4) { return 4; } if (opcode == OpCodes.Ldc_I4_5) { return 5; } if (opcode == OpCodes.Ldc_I4_6) { return 6; } if (opcode == OpCodes.Ldc_I4_7) { return 7; } if (opcode == OpCodes.Ldc_I4_8) { return 8; } if (opcode == OpCodes.Ldc_I4_S) { return instruction.operand as sbyte?; } if (opcode == OpCodes.Ldc_I4) { return instruction.operand as int?; } return null; } } } namespace PathfindingLagFix.Patches { [HarmonyPatch(typeof(CentipedeAI))] internal static class PatchCentipedeAI { private static bool useAsync = true; private const int HIDE_AWAY_FROM_MAIN_ID = 0; private const int HIDE_NEAR_PLAYER_ID = 1; private static bool ChooseHidingSpotRelativeToMainEntranceAsync(CentipedeAI centipede) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!useAsync) { return false; } int num = ((EnemyAI)centipede).allAINodes.Length; Transform val = AsyncDistancePathfinding.StartChoosingFarthestNodeFromPosition((EnemyAI)(object)centipede, 0, centipede.mainEntrancePosition, avoidLineOfSight: false, (num / 2 + ((EnemyAI)centipede).thisEnemyIndex) % num).RetrieveChosenNode(out ((EnemyAI)centipede).mostOptimalDistance); if ((Object)(object)val != (Object)null) { centipede.choseHidingSpotNoPlayersNearby = true; centipede.SetDestinationToNode(val); } return true; } [HarmonyTranspiler] [HarmonyPatch("DoAIInterval")] private static IEnumerable<CodeInstruction> DoAIIntervalTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown ILInjector iLInjector = new ILInjector(instructions).Find(ILMatcher.Ldarg(0), ILMatcher.Ldfld(typeof(CentipedeAI).GetField("choseHidingSpotNoPlayersNearby", BindingFlags.Instance | BindingFlags.NonPublic)), ILMatcher.Opcode(OpCodes.Brtrue)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find instructions to check if a hiding spot is chosen in CentipedeAI.DoAIInterval()."); return instructions; } Label label = (Label)iLInjector.LastMatchedInstruction.operand; return iLInjector.GoToMatchEnd().InsertInPlace(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchCentipedeAI), "ChooseHidingSpotRelativeToMainEntranceAsync", BindingFlags.Static | BindingFlags.NonPublic, new Type[1] { typeof(CentipedeAI) })), new CodeInstruction(OpCodes.Brtrue_S, (object)label)).ReleaseInstructions(); } private static bool ChooseHidingSpotNearPlayer(CentipedeAI centipede, Vector3 targetPos) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (!useAsync) { return false; } Transform val = AsyncDistancePathfinding.StartChoosingClosestNodeToPosition((EnemyAI)(object)centipede, 1, targetPos, avoidLineOfSight: true, centipede.offsetNodeAmount).RetrieveChosenNode(out ((EnemyAI)centipede).mostOptimalDistance); if ((Object)(object)val != (Object)null) { centipede.SetDestinationToNode(val); } return true; } [HarmonyTranspiler] [HarmonyPatch("ChooseHidingSpotNearPlayer")] private static IEnumerable<CodeInstruction> ChooseHidingSpotNearPlayerTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown ILInjector iLInjector = new ILInjector(instructions).Find(ILMatcher.Call(Reflection.m_EnemyAI_ChooseClosestNodeToPosition), ILMatcher.Stloc()); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find call to ChooseClosestNodeToPosition in CentipedeAI.ChooseHidingSpotNearPlayer()."); return instructions; } Label label = generator.DefineLabel(); return iLInjector.GoToPush(3).InsertAfterBranch(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldarg_1, (object)null), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchCentipedeAI), "ChooseHidingSpotNearPlayer", BindingFlags.Static | BindingFlags.NonPublic, new Type[2] { typeof(CentipedeAI), typeof(Vector3) })), new CodeInstruction(OpCodes.Brfalse_S, (object)label), new CodeInstruction(OpCodes.Ret, (object)null)).AddLabel(label) .ReleaseInstructions(); } } [HarmonyPatch(typeof(DoublewingAI))] internal static class PatchDoublewingAI { private const int EVADE_PLAYER_ID = 0; private static bool useAsync = true; private static PlayerControllerB GetPlayerToEvade(DoublewingAI doublewing) { return ((EnemyAI)doublewing).CheckLineOfSightForPlayer(80f, 8, 4); } private static AsyncDistancePathfinding.EnemyDistancePathfindingStatus StartEvasionPathfindingJob(DoublewingAI doublewing, PlayerControllerB player) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) return AsyncDistancePathfinding.StartChoosingFarthestNodeFromPosition((EnemyAI)(object)doublewing, 0, ((Component)player).transform.position, avoidLineOfSight: false, Random.Range(0, ((EnemyAI)doublewing).allAINodes.Length / 2)); } private static Transform ChoosePlayerEvasionNodeAsync(DoublewingAI doublewing, PlayerControllerB player) { return StartEvasionPathfindingJob(doublewing, player).RetrieveChosenNode(out ((EnemyAI)doublewing).mostOptimalDistance); } [HarmonyTranspiler] [HarmonyPatch("DoAIInterval")] private static IEnumerable<CodeInstruction> DoAIIntervalTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Expected O, but got Unknown //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Expected O, but got Unknown //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Expected O, but got Unknown //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Expected O, but got Unknown //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Expected O, but got Unknown ILInjector iLInjector = new ILInjector(instructions).Find(ILMatcher.Call(Reflection.m_EnemyAI_CheckLineOfSightForPlayer), ILMatcher.Stloc()); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find instructions to find the player to evade from in DoublewingAI.DoAIInterval()."); return instructions; } CodeInstruction val = iLInjector.LastMatchedInstruction.StlocToLdloc(); iLInjector.Find(ILMatcher.Ldloc(val.GetLdlocIndex()), ILMatcher.Call(Reflection.m_Object_op_Implicit), ILMatcher.Opcode(OpCodes.Brfalse)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find instructions to check if a player is in line of sight in DoublewingAI.DoAIInterval()."); return instructions; } Label label = (Label)iLInjector.LastMatchedInstruction.operand; iLInjector.GoToMatchEnd().Find(ILMatcher.Call(Reflection.m_EnemyAI_ChooseFarthestNodeFromPosition), ILMatcher.Stloc()); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find instructions to choose player evasion node in DoublewingAI.DoAIInterval()."); return instructions; } CodeInstruction lastMatchedInstruction = iLInjector.LastMatchedInstruction; Label label2 = generator.DefineLabel(); iLInjector.GetRelativeInstruction(2).labels.Add(label2); Label label3 = generator.DefineLabel(); return iLInjector.GoToPush(6).Insert(new CodeInstruction(OpCodes.Ldsfld, (object)typeof(PatchDoublewingAI).GetField("useAsync", BindingFlags.Static | BindingFlags.NonPublic)), new CodeInstruction(OpCodes.Brfalse_S, (object)label3), new CodeInstruction(OpCodes.Ldarg_0, (object)null), val, new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchDoublewingAI), "ChoosePlayerEvasionNodeAsync", BindingFlags.Static | BindingFlags.NonPublic, new Type[2] { typeof(DoublewingAI), typeof(PlayerControllerB) })), new CodeInstruction(OpCodes.Dup, (object)null), lastMatchedInstruction, new CodeInstruction(OpCodes.Ldnull, (object)null), new CodeInstruction(OpCodes.Call, (object)Reflection.m_Object_op_Equality), new CodeInstruction(OpCodes.Brtrue_S, (object)label), new CodeInstruction(OpCodes.Br, (object)label2)).AddLabel(label3) .ReleaseInstructions(); } [HarmonyPostfix] [HarmonyPatch("AlertBird")] [HarmonyPatch("AlertBirdByOther")] private static void AlertBirdPostfix(DoublewingAI __instance) { if (useAsync && ((NetworkBehaviour)__instance).IsOwner) { PlayerControllerB playerToEvade = GetPlayerToEvade(__instance); if (playerToEvade != null) { StartEvasionPathfindingJob(__instance, playerToEvade); } } } } [HarmonyPatch(typeof(EnemyAI))] internal static class PatchEnemyAI { private static bool useAsyncRoaming = true; private static bool useAsyncPlayerPaths = false; private static bool StopPreviousJobAndStartNewOne(EnemyAI enemy) { if (!useAsyncRoaming) { return false; } AsyncRoamingPathfinding.EnemyRoamingPathfindingStatus status = AsyncRoamingPathfinding.GetStatus(enemy); if (!((JobHandle)(ref status.PathsFromEnemyJobHandle)).IsCompleted || !((JobHandle)(ref status.PathsFromSearchStartJobHandle)).IsCompleted) { status.PathsFromEnemyJob.Cancel(); status.PathsFromSearchStartJob.Cancel(); return true; } status.StartJobs(enemy); return false; } private static PathQueryStatus GetPathStatus(EnemyAI enemy, int index) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Invalid comparison between Unknown and I4 //IL_007b: Unknown result type (might be due to invalid IL or missing references) if (!useAsyncRoaming) { return (PathQueryStatus)1073741824; } AsyncRoamingPathfinding.EnemyRoamingPathfindingStatus status = AsyncRoamingPathfinding.GetStatus(enemy); if (!status.PathsFromEnemyJob.Statuses.IsCreated) { StopPreviousJobAndStartNewOne(enemy); } int jobIndex = status.GetJobIndex(index); PathQueryStatus result = PathQueryStatusExtensions.GetResult(status.PathsFromEnemyJob.Statuses[jobIndex]); if (!enemy.currentSearch.startedSearchAtSelf && (int)PathQueryStatusExtensions.GetResult(status.PathsFromSearchStartJob.Statuses[jobIndex]) == 536870912) { result = (PathQueryStatus)536870912; } return result; } private static void SetPathDistance(EnemyAI enemy, int index) { AsyncRoamingPathfinding.EnemyRoamingPathfindingStatus status = AsyncRoamingPathfinding.GetStatus(enemy); FindPathsToNodesJob findPathsToNodesJob = (enemy.currentSearch.startedSearchAtSelf ? status.PathsFromEnemyJob : status.PathsFromSearchStartJob); if (findPathsToNodesJob.PathDistances.IsCreated) { enemy.pathDistance = findPathsToNodesJob.PathDistances[status.GetJobIndex(index)]; } else { enemy.pathDistance = 0f; } } private static void CancelJobs(EnemyAI enemy) { AsyncRoamingPathfinding.EnemyRoamingPathfindingStatus status = AsyncRoamingPathfinding.GetStatus(enemy); status.PathsFromEnemyJob.Cancel(); status.PathsFromSearchStartJob.Cancel(); } [HarmonyTranspiler] [HarmonyPatch(/*Could not decode attribute arguments.*/)] private static IEnumerable<CodeInstruction> ChooseNextNodeInSearchRoutineTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase method, ILGenerator generator) { //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Expected O, but got Unknown //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Expected O, but got Unknown //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Expected O, but got Unknown //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Expected O, but got Unknown //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Expected O, but got Unknown //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Expected O, but got Unknown //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Expected O, but got Unknown //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Expected O, but got Unknown //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Expected O, but got Unknown //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Expected O, but got Unknown //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Expected O, but got Unknown //IL_0309: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Expected O, but got Unknown //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Expected O, but got Unknown //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Expected O, but got Unknown //IL_0369: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Expected O, but got Unknown //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Expected O, but got Unknown //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Expected O, but got Unknown //IL_03a2: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Expected O, but got Unknown //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Expected O, but got Unknown //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03c6: Expected O, but got Unknown //IL_03cf: Unknown result type (might be due to invalid IL or missing references) //IL_03d5: Expected O, but got Unknown //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Expected O, but got Unknown //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Expected O, but got Unknown //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Expected O, but got Unknown //IL_040b: Unknown result type (might be due to invalid IL or missing references) //IL_0411: Expected O, but got Unknown //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Expected O, but got Unknown //IL_0447: Unknown result type (might be due to invalid IL or missing references) //IL_044d: Expected O, but got Unknown //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_0462: Expected O, but got Unknown //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0471: Expected O, but got Unknown //IL_047a: Unknown result type (might be due to invalid IL or missing references) //IL_0480: Expected O, but got Unknown //IL_0489: Unknown result type (might be due to invalid IL or missing references) //IL_048f: Expected O, but got Unknown //IL_049c: Unknown result type (might be due to invalid IL or missing references) //IL_04a2: Expected O, but got Unknown //IL_04b1: Unknown result type (might be due to invalid IL or missing references) //IL_04b7: Expected O, but got Unknown //IL_05bf: Unknown result type (might be due to invalid IL or missing references) //IL_05c5: Expected O, but got Unknown //IL_05d3: Unknown result type (might be due to invalid IL or missing references) //IL_05d9: Expected O, but got Unknown //IL_069d: Unknown result type (might be due to invalid IL or missing references) //IL_06a3: Expected O, but got Unknown //IL_06b1: Unknown result type (might be due to invalid IL or missing references) //IL_06b7: Expected O, but got Unknown //IL_06bf: Unknown result type (might be due to invalid IL or missing references) //IL_06c5: Expected O, but got Unknown //IL_06cd: Unknown result type (might be due to invalid IL or missing references) //IL_06d3: Expected O, but got Unknown //IL_06db: Unknown result type (might be due to invalid IL or missing references) //IL_06e1: Expected O, but got Unknown //IL_071e: Unknown result type (might be due to invalid IL or missing references) //IL_0724: Expected O, but got Unknown //IL_0732: Unknown result type (might be due to invalid IL or missing references) //IL_0738: Expected O, but got Unknown //IL_0781: Unknown result type (might be due to invalid IL or missing references) //IL_0787: Expected O, but got Unknown //IL_07b7: Unknown result type (might be due to invalid IL or missing references) //IL_07bd: Expected O, but got Unknown FieldInfo fieldInfo = null; FieldInfo fieldInfo2 = null; FieldInfo fieldInfo3 = null; FieldInfo[] fields = method.DeclaringType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo4 in fields) { if (fieldInfo == null && fieldInfo4.Name.EndsWith("state")) { fieldInfo = fieldInfo4; } else if (fieldInfo2 == null && fieldInfo4.Name.EndsWith("current")) { fieldInfo2 = fieldInfo4; } else if (fieldInfo3 == null && fieldInfo4.Name.StartsWith("<i>")) { fieldInfo3 = fieldInfo4; } } if (fieldInfo == null) { Plugin.Instance.Logger.LogError((object)"Failed to find the 'state' field in the enumerator for EnemyAI.ChooseNextNodeInSearchRoutine()."); return instructions; } if (fieldInfo2 == null) { Plugin.Instance.Logger.LogError((object)"Failed to find the 'current' field in the enumerator for EnemyAI.ChooseNextNodeInSearchRoutine()."); return instructions; } if (fieldInfo3 == null) { Plugin.Instance.Logger.LogError((object)"Failed to find the 'i' field in the enumerator for EnemyAI.ChooseNextNodeInSearchRoutine()."); return instructions; } ILInjector iLInjector = new ILInjector(instructions).Find(ILMatcher.Opcode(OpCodes.Switch)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the switch in the enumerator for EnemyAI.ChooseNextNodeInSearchRoutine()."); return instructions; } Label[] array = (Label[])iLInjector.Instruction.operand; iLInjector.FindLabel(array[0]); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the switch case 0 in the enumerator for EnemyAI.ChooseNextNodeInSearchRoutine()."); return instructions; } Label label = generator.DefineLabel(); iLInjector.InsertAfterBranch(new CodeInstruction(OpCodes.Ldloc_1, (object)null), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchEnemyAI), "StopPreviousJobAndStartNewOne", BindingFlags.Static | BindingFlags.NonPublic, new Type[1] { typeof(EnemyAI) })), new CodeInstruction(OpCodes.Brfalse_S, (object)label), new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldc_I4_0, (object)null), new CodeInstruction(OpCodes.Stfld, (object)fieldInfo), new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldnull, (object)null), new CodeInstruction(OpCodes.Stfld, (object)fieldInfo2), new CodeInstruction(OpCodes.Ldc_I4_1, (object)null), new CodeInstruction(OpCodes.Ret, (object)null)).AddLabel(label); iLInjector.FindLabel(array[2]).Find(ILMatcher.Ldarg(0), ILMatcher.Ldc(-1), ILMatcher.Stfld(fieldInfo)).GoToMatchEnd(); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the switch case 2 in the enumerator for EnemyAI.ChooseNextNodeInSearchRoutine()."); return instructions; } LocalBuilder localBuilder = generator.DeclareLocal(typeof(PathQueryStatus)); Label label2 = generator.DefineLabel(); Label label3 = generator.DefineLabel(); Label label4 = generator.DefineLabel(); iLInjector.InsertAfterBranch(new CodeInstruction(OpCodes.Ldloc_1, (object)null), new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo3), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchEnemyAI), "GetPathStatus", BindingFlags.Static | BindingFlags.NonPublic, new Type[2] { typeof(EnemyAI), typeof(int) })), new CodeInstruction(OpCodes.Dup, (object)null), new CodeInstruction(OpCodes.Stloc, (object)localBuilder), new CodeInstruction(OpCodes.Ldc_I4, (object)536870912), new CodeInstruction(OpCodes.Bne_Un_S, (object)label2), new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldc_I4_2, (object)null), new CodeInstruction(OpCodes.Stfld, (object)fieldInfo), new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldnull, (object)null), new CodeInstruction(OpCodes.Stfld, (object)fieldInfo2), new CodeInstruction(OpCodes.Ldc_I4_1, (object)null), new CodeInstruction(OpCodes.Ret, (object)null), CodeInstructionExtensions.WithLabels(new CodeInstruction(OpCodes.Ldloc, (object)localBuilder), new Label[1] { label2 }), new CodeInstruction(OpCodes.Ldc_I4, (object)int.MinValue), new CodeInstruction(OpCodes.Bne_Un_S, (object)label3), new CodeInstruction(OpCodes.Ldloc_1, (object)null), new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo3), new CodeInstruction(OpCodes.Call, (object)Reflection.m_EnemyAI_EliminateNodeFromSearch), new CodeInstruction(OpCodes.Br, (object)label4)).AddLabel(label3); iLInjector.Find(ILMatcher.Call(Reflection.m_EnemyAI_PathIsIntersectedByLineOfSight)).Find(ILMatcher.Call(Reflection.m_EnemyAI_EliminateNodeFromSearch), ILMatcher.Opcode(OpCodes.Br)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find where AI nodes are eliminated for failing to find a path in the enumerator for EnemyAI.ChooseNextNodeInSearchRoutine()."); return instructions; } Label label5 = generator.DefineLabel(); Label label6 = (Label)iLInjector.LastMatchedInstruction.operand; iLInjector.GoToMatchEnd().AddLabel(label5).Back(2) .ReverseFind(ILMatcher.Call(Reflection.m_EnemyAI_EliminateNodeFromSearch), ILMatcher.Opcode(OpCodes.Br)) .GoToMatchEnd(); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the instruction before a path is tested in the enumerator for EnemyAI.ChooseNextNodeInSearchRoutine()."); return instructions; } FieldInfo field = typeof(PatchEnemyAI).GetField("useAsyncRoaming", BindingFlags.Static | BindingFlags.NonPublic); iLInjector.InsertAfterBranch(new CodeInstruction(OpCodes.Ldsfld, (object)field), new CodeInstruction(OpCodes.Brtrue_S, (object)label5)); iLInjector.Find(ILMatcher.Call(Reflection.m_EnemyAI_GetPathDistance), ILMatcher.Opcode(OpCodes.Pop)).ReverseFind(ILMatcher.Ldloc(1), ILMatcher.Ldfld(Reflection.f_EnemyAI_currentSearch), ILMatcher.Ldfld(typeof(AISearchRoutine).GetField("startedSearchAtSelf")), ILMatcher.Opcode(OpCodes.Brtrue)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the call to get the path distance in EnemyAI.ChooseNextNodeInSearchRoutine()."); return instructions; } Label label7 = (Label)iLInjector.LastMatchedInstruction.operand; Label label8 = generator.DefineLabel(); iLInjector.InsertAfterBranch(new CodeInstruction(OpCodes.Ldsfld, (object)field), new CodeInstruction(OpCodes.Brfalse_S, (object)label8), new CodeInstruction(OpCodes.Ldloc_1, (object)null), new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo3), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchEnemyAI), "SetPathDistance", BindingFlags.Static | BindingFlags.NonPublic, new Type[2] { typeof(EnemyAI), typeof(int) })), new CodeInstruction(OpCodes.Br, (object)label7)).AddLabel(label8); iLInjector.FindLabel(label6).AddLabel(label4); return iLInjector.GoToEnd().ReverseFind(ILMatcher.Opcode(OpCodes.Ret)).Insert(new CodeInstruction(OpCodes.Ldloc_1, (object)null), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchEnemyAI), "CancelJobs", BindingFlags.Static | BindingFlags.NonPublic, new Type[1] { typeof(EnemyAI) }))) .ReleaseInstructions(); } private static bool IsPathToPlayerInvalid(EnemyAI enemy, int playerIndex) { AsyncPlayerPathfinding.EnemyToPlayerPathfindingStatus status = AsyncPlayerPathfinding.GetStatus(enemy); if (!status.hasStarted) { status.StartJobs(enemy); return true; } return !status.IsPathValid(playerIndex); } private static void ResetPathToPlayerStatus(EnemyAI enemy) { AsyncPlayerPathfinding.EnemyToPlayerPathfindingStatus status = AsyncPlayerPathfinding.GetStatus(enemy); status.ResetIfResultsHaveBeenUsed(); if (!status.hasStarted && useAsyncPlayerPaths) { status.StartJobs(enemy); } } private static IEnumerator ResetPathToPlayerStatusAtEndOfFrame(EnemyAI enemy) { yield return (object)new WaitForEndOfFrame(); ResetPathToPlayerStatus(enemy); } [HarmonyTranspiler] [HarmonyPatch("TargetClosestPlayer")] private static IEnumerable<CodeInstruction> TargetClosestPlayerTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Expected O, but got Unknown //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Expected O, but got Unknown //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Expected O, but got Unknown //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Expected O, but got Unknown //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Expected O, but got Unknown //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Expected O, but got Unknown //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Expected O, but got Unknown ILInjector iLInjector = new ILInjector(instructions).Find(ILMatcher.Ldarg(0), ILMatcher.Call(typeof(StartOfRound).GetMethod("get_Instance")), ILMatcher.Ldfld(typeof(StartOfRound).GetField("allPlayerScripts")), ILMatcher.Ldloc(), ILMatcher.Opcode(OpCodes.Ldelem_Ref), ILMatcher.Callvirt(Reflection.m_Component_get_transform), ILMatcher.Callvirt(Reflection.m_Transform_get_position), ILMatcher.Ldc(0), ILMatcher.Ldc(0), ILMatcher.Ldc(0), ILMatcher.Call(Reflection.m_EnemyAI_PathIsIntersectedByLineOfSight)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the call to check if the enemy can path to a player in EnemyAI.TargetClosestPlayer()."); return instructions; } CodeInstruction relativeInstruction = iLInjector.GetRelativeInstruction(3); Label label = generator.DefineLabel(); Label label2 = generator.DefineLabel(); iLInjector.Insert(new CodeInstruction(OpCodes.Ldsfld, (object)typeof(PatchEnemyAI).GetField("useAsyncPlayerPaths", BindingFlags.Static | BindingFlags.NonPublic)), new CodeInstruction(OpCodes.Brfalse_S, (object)label), new CodeInstruction(OpCodes.Ldarg_0, (object)null), relativeInstruction, new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchEnemyAI), "IsPathToPlayerInvalid", BindingFlags.Static | BindingFlags.NonPublic, new Type[2] { typeof(EnemyAI), typeof(int) })), new CodeInstruction(OpCodes.Br_S, (object)label2)).AddLabel(label).GoToMatchEnd() .AddLabel(label2); iLInjector.Find(ILMatcher.Ldarg(0), ILMatcher.Ldfld(Reflection.f_EnemyAI_targetPlayer)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the check for whether a target player was found in EnemyAI.TargetClosestPlayer()."); return instructions; } Label label3 = generator.DefineLabel(); return iLInjector.Insert(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchEnemyAI), "ResetPathToPlayerStatusAtEndOfFrame", BindingFlags.Static | BindingFlags.NonPublic, new Type[1] { typeof(EnemyAI) })), new CodeInstruction(OpCodes.Call, (object)Reflection.m_MonoBehaviour_StartCoroutine), new CodeInstruction(OpCodes.Pop, (object)null)).AddLabel(label3).ReleaseInstructions(); } [HarmonyPostfix] [HarmonyPatch("OnDestroy")] private static void OnDestroyPostfix(EnemyAI __instance) { AsyncDistancePathfinding.RemoveStatus(__instance); AsyncPlayerPathfinding.RemoveStatus(__instance); AsyncRoamingPathfinding.RemoveStatus(__instance); } } [HarmonyPatch(typeof(FlowermanAI))] internal static class PatchFlowermanAI { private static readonly FieldInfo f_FlowermanAI_mainEntrancePosition = AccessTools.Field(typeof(FlowermanAI), "mainEntrancePosition"); private static readonly MethodInfo m_FlowermanAI_ChooseClosestNodeToPlayer = typeof(FlowermanAI).GetMethod("ChooseClosestNodeToPlayer", Array.Empty<Type>()); private static readonly FieldInfo f_PatchFlowermanAI_useAsync = typeof(PatchFlowermanAI).GetField("useAsync"); private static bool useAsync = true; private const int FAR_FROM_MAIN_ID = 0; private const int EVADE_PLAYER_ID = 1; private const int SNEAK_TO_PLAYER_ID = 2; private static bool ChooseFarthestNodeFromMainEntranceAsync(FlowermanAI flowerman) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (useAsync) { Transform val = AsyncDistancePathfinding.StartChoosingFarthestNodeFromPosition((EnemyAI)(object)flowerman, 0, flowerman.mainEntrancePosition).RetrieveChosenNode(out ((EnemyAI)flowerman).mostOptimalDistance); if ((Object)(object)val != (Object)null) { if ((Object)(object)((EnemyAI)flowerman).favoriteSpot == (Object)null) { ((EnemyAI)flowerman).favoriteSpot = val; } ((EnemyAI)flowerman).targetNode = val; ((EnemyAI)flowerman).SetDestinationToPosition(val.position, false); } return true; } return false; } [HarmonyTranspiler] [HarmonyPatch("DoAIInterval")] private static IEnumerable<CodeInstruction> DoAIIntervalTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown ILInjector iLInjector = new ILInjector(instructions).Find(ILMatcher.Call(m_FlowermanAI_ChooseClosestNodeToPlayer), ILMatcher.Branch()).GoToMatchEnd(); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find call to ChooseClosestNodeToPlayer in FlowermanAI.DoAIInterval()."); return instructions; } Label label = (Label)iLInjector.GetRelativeInstruction(-1).operand; iLInjector.Find(ILMatcher.Ldarg(0), ILMatcher.Ldarg(0), ILMatcher.Ldfld(typeof(FlowermanAI).GetField("mainEntrancePosition", BindingFlags.Instance | BindingFlags.NonPublic)), ILMatcher.Ldc(0), ILMatcher.Ldc(0), ILMatcher.Ldc(0), ILMatcher.Ldc(50), ILMatcher.Ldc(0), ILMatcher.Call(Reflection.m_EnemyAI_ChooseFarthestNodeFromPosition)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find call to ChooseFarthestNodeFromPosition in FlowermanAI.DoAIInterval()."); return instructions; } return iLInjector.InsertInPlaceAfterBranch(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchFlowermanAI), "ChooseFarthestNodeFromMainEntranceAsync", BindingFlags.Static | BindingFlags.NonPublic, new Type[1] { typeof(FlowermanAI) })), new CodeInstruction(OpCodes.Brtrue_S, (object)label)).ReleaseInstructions(); } private static bool ChoosePlayerEvasionNodeAsync(FlowermanAI flowerman) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!useAsync) { return false; } Transform val = AsyncDistancePathfinding.StartChoosingFarthestNodeFromPosition((EnemyAI)(object)flowerman, 1, ((Component)((EnemyAI)flowerman).targetPlayer).transform.position, avoidLineOfSight: true, 0, 60f).RetrieveChosenNode(out ((EnemyAI)flowerman).mostOptimalDistance); if ((Object)(object)val == (Object)null) { return true; } flowerman.farthestNodeFromTargetPlayer = val; return false; } [HarmonyTranspiler] [HarmonyPatch("AvoidClosestPlayer")] private static IEnumerable<CodeInstruction> AvoidClosestPlayerTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown ILInjector iLInjector = new ILInjector(instructions).Find(ILMatcher.Ldarg(0), ILMatcher.Ldfld(typeof(FlowermanAI).GetField("farthestNodeFromTargetPlayer", BindingFlags.Instance | BindingFlags.NonPublic)), ILMatcher.Opcode(OpCodes.Ldnull), ILMatcher.Call(Reflection.m_Object_op_Equality), ILMatcher.Opcode(OpCodes.Brfalse)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find check for the farthest node from the target player in FlowermanAI.AvoidClosestPlayer()."); return instructions; } Label label = generator.DefineLabel(); return iLInjector.AddLabel(label).InsertInPlace(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchFlowermanAI), "ChoosePlayerEvasionNodeAsync", BindingFlags.Static | BindingFlags.NonPublic, new Type[1] { typeof(FlowermanAI) })), new CodeInstruction(OpCodes.Brfalse_S