Decompiled source of PathfindingLagFix v2.1.1
PathfindingLagFix.dll
Decompiled 2 weeks 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.Configuration; 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 Unity.Profiling; using Unity.Profiling.LowLevel; using Unity.Profiling.LowLevel.Unsafe; using UnityEngine; using UnityEngine.AI; using UnityEngine.Experimental.AI; using UnityEngine.Scripting; [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.1.1")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.1.1.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] internal sealed class IsUnmanagedAttribute : Attribute { } [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 { internal struct ConfigOptions { private enum ConfigPreset { OnlyFixes, Vanilla } private static readonly ConfigOptions OnlyFixesPreset = new ConfigOptions { DistancePathfindingFallbackNodeSelection = DistancePathfindingFallbackNodeSelectionType.BestPathable, AsyncDistancePathfindingMostOptimalDistanceBehavior = AsyncDistancePathfindingMostOptimalDistanceBehaviorType.Set }; private static readonly ConfigOptions VanillaPreset = new ConfigOptions { DistancePathfindingFallbackNodeSelection = DistancePathfindingFallbackNodeSelectionType.Vanilla, AsyncDistancePathfindingMostOptimalDistanceBehavior = AsyncDistancePathfindingMostOptimalDistanceBehaviorType.DontSet }; internal static ConfigOptions CurrentOptions = OnlyFixesPreset; private const string presetDescription = "Select a preset to use as defaults for all options that change gameplay.\n\nOnlyFixes: Options that are intended to act solely as bug fixes and should retain the intention of the original code.\nVanilla: Options that completely match vanilla as much as possible."; private static ConfigEntry<ConfigPreset> presetOption; private const string distancePathfindingFallbackNodeSelectionDescription = "How nodes should be selected if the criteria for a distance based pathfinding operation (i.e. bracken evasion) fails.\n\nUsePreset: Use the option selected by the current preset.\nBestPathable: The enemy will go to the furthest/closest node that can be reached. This is the old behavior of PathfindingLagFix, and it guarantees the bracken will not get stuck when spotted.\nVanilla: The enemy will attempt to go to the furthest/closest node, regardless of whether it can be reached. This will cause brackens to sometimes stutter step towards the furthest position instead of moving smoothly.\nDontMove: The enemy will not move until it has a valid path to follow. For the bracken, this will result in it standing still and looking at the player until they look away."; private static ConfigEntry<DistancePathfindingFallbackNodeSelectionType> distancePathfindingFallbackNodeSelectionOption; internal DistancePathfindingFallbackNodeSelectionType DistancePathfindingFallbackNodeSelection; private const string asyncDistancePathfindingMostOptimalDistanceBehaviorDescription = "Selects whether to set the mostOptimalDistance field containing the distance to the selected node at the site where the vanilla async distance pathfinding is used.\n\nWhen enabled, this will fix the vanilla bug where the bracken will stand still or walk slowly towards a player instead of retreating if it is spotted within 5 units."; private static ConfigEntry<AsyncDistancePathfindingMostOptimalDistanceBehaviorType> asyncDistancePathfindingMostOptimalDistanceBehaviorOption; internal AsyncDistancePathfindingMostOptimalDistanceBehaviorType AsyncDistancePathfindingMostOptimalDistanceBehavior; internal static void BindAllOptions(ConfigFile file) { presetOption = BindOption(file, "General", "Preset", ConfigPreset.OnlyFixes, "Select a preset to use as defaults for all options that change gameplay.\n\nOnlyFixes: Options that are intended to act solely as bug fixes and should retain the intention of the original code.\nVanilla: Options that completely match vanilla as much as possible."); distancePathfindingFallbackNodeSelectionOption = BindOption(file, "Behavior", "DistancePathfindingFallbackNodeSelection", DistancePathfindingFallbackNodeSelectionType.UsePreset, "How nodes should be selected if the criteria for a distance based pathfinding operation (i.e. bracken evasion) fails.\n\nUsePreset: Use the option selected by the current preset.\nBestPathable: The enemy will go to the furthest/closest node that can be reached. This is the old behavior of PathfindingLagFix, and it guarantees the bracken will not get stuck when spotted.\nVanilla: The enemy will attempt to go to the furthest/closest node, regardless of whether it can be reached. This will cause brackens to sometimes stutter step towards the furthest position instead of moving smoothly.\nDontMove: The enemy will not move until it has a valid path to follow. For the bracken, this will result in it standing still and looking at the player until they look away."); asyncDistancePathfindingMostOptimalDistanceBehaviorOption = BindOption(file, "Behavior", "AsyncDistancePathfindingMostOptimalDistanceBehavior", AsyncDistancePathfindingMostOptimalDistanceBehaviorType.UsePreset, "Selects whether to set the mostOptimalDistance field containing the distance to the selected node at the site where the vanilla async distance pathfinding is used.\n\nWhen enabled, this will fix the vanilla bug where the bracken will stand still or walk slowly towards a player instead of retreating if it is spotted within 5 units."); UpdateCurrentOptions(); } private static ConfigEntry<T> BindOption<T>(ConfigFile file, string section, string key, T defaultValue, string description) { ConfigEntry<T> obj = file.Bind<T>(section, key, defaultValue, description); obj.SettingChanged += delegate { UpdateCurrentOptions(); }; return obj; } private static void UpdateCurrentOptions() { switch (presetOption.Value) { case ConfigPreset.OnlyFixes: CurrentOptions = OnlyFixesPreset; break; case ConfigPreset.Vanilla: CurrentOptions = VanillaPreset; break; default: throw new InvalidOperationException($"Unknown preset {presetOption.Value}"); } if (distancePathfindingFallbackNodeSelectionOption.Value != 0) { CurrentOptions.DistancePathfindingFallbackNodeSelection = distancePathfindingFallbackNodeSelectionOption.Value; } if (asyncDistancePathfindingMostOptimalDistanceBehaviorOption.Value != 0) { CurrentOptions.AsyncDistancePathfindingMostOptimalDistanceBehavior = asyncDistancePathfindingMostOptimalDistanceBehaviorOption.Value; } } } internal enum DistancePathfindingFallbackNodeSelectionType { UsePreset, BestPathable, Vanilla, DontMove } internal enum AsyncDistancePathfindingMostOptimalDistanceBehaviorType { UsePreset, Set, DontSet } [BepInPlugin("Zaggy1024.PathfindingLagFix", "PathfindingLagFix", "2.1.1")] [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.1.1"; 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; ConfigOptions.BindAllOptions(((BaseUnityPlugin)this).Config); 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.RetrieveChosenNode(out var _); 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) { continue; } } 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) { break; } } job.Cancel(); if (result == -1) { switch (ConfigOptions.CurrentOptions.DistancePathfindingFallbackNodeSelection) { case DistancePathfindingFallbackNodeSelectionType.BestPathable: { for (int j = 0; j < candidateCount; j++) { if ((int)PathQueryStatusExtensions.GetResult(job.Statuses[j]) == 1073741824) { result = j; break; } } break; } case DistancePathfindingFallbackNodeSelectionType.Vanilla: result = 0; break; } } if (result == -1) { status.ChosenNode = ((Component)enemy).transform; status.MostOptimalDistance = 0f; } else { 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 enum JobsStatus { NotStarted, NotRetrieved, RetrievedJobsDone, RetrievedJobsIncomplete } internal FindPathsToNodesJob PathsToPlayersJob; internal JobHandle PathsToPlayersJobHandle; private int[] playerJobIndices = Array.Empty<int>(); private readonly List<Vector3> validPlayerPositions = new List<Vector3>(); private float inFlightJobsTime = float.NegativeInfinity; private float currentJobsTime = float.NegativeInfinity; private bool[] playersPathable = Array.Empty<bool>(); private void StartJobs(EnemyAI enemy) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: 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) 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(); 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)); inFlightJobsTime = Time.time; } internal bool AllJobsAreDone() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 for (int i = 0; i < validPlayerPositions.Count; i++) { if ((int)PathQueryStatusExtensions.GetResult(PathsToPlayersJob.Statuses[i]) == 536870912) { return false; } } return true; } internal float UpdatePathsAndGetCalculationTime(EnemyAI enemy) { //IL_005c: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Invalid comparison between Unknown and I4 if (!AllJobsAreDone()) { return currentJobsTime; } if (playersPathable.Length != playerJobIndices.Length) { Array.Resize(ref playersPathable, playerJobIndices.Length); } for (int i = 0; i < playerJobIndices.Length; i++) { int num = playerJobIndices[i]; if (num == -1) { playersPathable[i] = false; continue; } PathQueryStatus result = PathQueryStatusExtensions.GetResult(PathsToPlayersJob.Statuses[num]); playersPathable[i] = (int)result == 1073741824; } currentJobsTime = inFlightJobsTime; StartJobs(enemy); return currentJobsTime; } internal bool CanReachPlayer(int playerIndex) { return playersPathable[playerIndex]; } internal void Clear() { playerJobIndices = Array.Empty<int>(); validPlayerPositions.Clear(); playersPathable = Array.Empty<bool>(); } ~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 Vector3[] nodePositions = Array.Empty<Vector3>(); 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: 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; if (nodeCount > nodePositions.Length) { nodePositions = (Vector3[])(object)new Vector3[nodeCount]; } for (int i = 0; i < unsearchedNodes.Count; i++) { nodePositions[GetJobIndex(i)] = unsearchedNodes[i].transform.position; } PathsFromEnemyJob.Initialize(agent.agentTypeID, agent.areaMask, pathOrigin, nodePositions, nodeCount, currentSearch.startedSearchAtSelf); PathsFromEnemyJobHandle = IJobForExtensions.ScheduleByRef<FindPathsToNodesJob>(ref PathsFromEnemyJob, unsearchedNodes.Count, default(JobHandle)); if (!currentSearch.startedSearchAtSelf) { PathsFromSearchStartJob.Initialize(agent.agentTypeID, agent.areaMask, currentSearch.currentSearchStartPosition, nodePositions, nodeCount, 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_003c: 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_0050: 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); Statuses.SetAllElements<PathQueryStatus>((PathQueryStatus)536870912); if (calculateDistance) { PathDistances.SetAllElements(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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (Canceled.Length != 1) { 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_0051: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Invalid comparison between Unknown and I4 //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Invalid comparison between Unknown and I4 //IL_0118: 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_014d: 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_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Invalid comparison between Unknown and I4 //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0196: 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_019d: 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_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: 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_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Invalid comparison between Unknown and I4 //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_0272: 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_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) if (Canceled[0]) { Statuses[index] = (PathQueryStatus)int.MinValue; return; } NavMeshReadLocker val = default(NavMeshReadLocker); ((NavMeshReadLocker)(ref val))..ctor(); try { NavMeshQuery val2 = ThreadQueriesRef[ThreadIndex]; Vector3 val3 = new Vector3(5f, 5f, 5f); NavMeshLocation val4 = ((NavMeshQuery)(ref val2)).MapLocation(Origin, val3, AgentTypeID, AreaMask); if (!((NavMeshQuery)(ref val2)).IsValid(val4)) { Statuses[index] = (PathQueryStatus)int.MinValue; return; } Vector3 val5 = Destinations[index]; Vector3 val6 = new Vector3(1.5f, 1.5f, 1.5f); NavMeshLocation val7 = ((NavMeshQuery)(ref val2)).MapLocation(val5, val6, AgentTypeID, AreaMask); if (!((NavMeshQuery)(ref val2)).IsValid(val7)) { Statuses[index] = (PathQueryStatus)int.MinValue; return; } PathQueryStatus val8 = ((NavMeshQuery)(ref val2)).BeginFindPath(val4, val7, AreaMask, default(NativeArray<float>)); if ((int)PathQueryStatusExtensions.GetResult(val8) == int.MinValue) { Statuses[index] = val8; return; } int num = default(int); while ((int)PathQueryStatusExtensions.GetResult(val8) == 536870912) { val8 = ((NavMeshQuery)(ref val2)).UpdateFindPath(NavMeshLock.RecommendedUpdateFindPathIterationCount, ref num); ((NavMeshReadLocker)(ref val)).Yield(); } int num2 = default(int); val8 = ((NavMeshQuery)(ref val2)).EndFindPath(ref num2); if ((int)PathQueryStatusExtensions.GetResult(val8) != 1073741824) { Statuses[index] = val8; return; } NativeArray<PolygonId> val9 = new NativeArray<PolygonId>(num2, (Allocator)2, (NativeArrayOptions)1); ((NavMeshQuery)(ref val2)).GetPathResult(NativeSlice<PolygonId>.op_Implicit(val9)); int num3 = default(int); val8 = (PathQueryStatus)(NavMeshQueryUtils.FindStraightPath(val2, float3.op_Implicit(Origin), float3.op_Implicit(val5), NativeSlice<PolygonId>.op_Implicit(val9), num2, GetPathBuffer(index), ref num3) | PathQueryStatusExtensions.GetDetail(val8)); ((NavMeshReadLocker)(ref val)).Dispose(); PathSizes[index] = num3; val9.Dispose(); if ((int)PathQueryStatusExtensions.GetResult(val8) != 1073741824) { Statuses[index] = val8; return; } NativeArray<NavMeshLocation> path = GetPath(index); NavMeshLocation val10 = path[path.Length - 1]; Vector3 val11 = ((NavMeshLocation)(ref val10)).position - val5; if (((Vector3)(ref val11)).sqrMagnitude > 2.25f) { Statuses[index] = (PathQueryStatus)(int.MinValue | PathQueryStatusExtensions.GetDetail(val8)); return; } if (CalculateDistance) { float num4 = 0f; for (int i = 1; i < path.Length; i++) { float num5 = num4; val10 = path[i - 1]; Vector3 position = ((NavMeshLocation)(ref val10)).position; val10 = path[i]; num4 = num5 + Vector3.Distance(position, ((NavMeshLocation)(ref val10)).position); } PathDistances[index] = num4; } Statuses[index] = val8; } finally { ((IDisposable)(NavMeshReadLocker)(ref val)).Dispose(); } } 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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0029: 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_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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (path.Length <= 1) { return false; } for (int i = 1; i < path.Length && i < 16; i++) { NavMeshLocation val = path[i - 1]; Vector3 position = ((NavMeshLocation)(ref val)).position; val = path[i]; Vector3 position2 = ((NavMeshLocation)(ref val)).position; if (checkLOSToPosition.HasValue) { Vector3 val2 = (position + position2) * 0.5f; Vector3 val3 = Vector3.up * 0.25f; if (!Physics.Linecast(val2 + val3, checkLOSToPosition.Value + val3, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { return true; } } if (Physics.Linecast(position, position2, 262144)) { return true; } } return false; } } internal static class NativeArrayUtils { internal unsafe static void SetAllElements<T>(this NativeArray<T> array, T value) where T : unmanaged { //IL_0000: Unknown result type (might be due to invalid IL or missing references) T* buffer = (T*)array.m_Buffer; int length = array.Length; for (int i = 0; i < length; i++) { buffer[i] = value; } } } [IgnoredByDeepProfiler] [UsedByNativeCode] internal struct ProfilerMarkerWithMetadata<T> where T : unmanaged { [IgnoredByDeepProfiler] [UsedByNativeCode] public struct AutoScope : IDisposable { [NativeDisableUnsafePtrRestriction] internal readonly ProfilerMarkerWithMetadata<T> marker; internal T value; internal bool on; [MethodImpl(MethodImplOptions.AggressiveInlining)] public AutoScope(in ProfilerMarkerWithMetadata<T> marker, T value) { on = false; this.marker = marker; this.value = value; Resume(); } public void Resume() { if (!on) { on = true; marker.Begin(in value); } } public void Pause() { if (on) { marker.End(); on = false; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { Pause(); } } private readonly IntPtr ptr; private ProfilerMarkerData data; public ProfilerMarkerWithMetadata(string name, string metadata) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) data = default(ProfilerMarkerData); ptr = ProfilerUnsafeUtility.CreateMarker(name, (ushort)1, (MarkerFlags)0, 0); if (typeof(T) == typeof(int)) { data.Type = 2; } else if (typeof(T) == typeof(long)) { data.Type = 4; } else if (typeof(T) == typeof(uint)) { data.Type = 3; } else if (typeof(T) == typeof(ulong)) { data.Type = 5; } else if (typeof(T) == typeof(float)) { data.Type = 6; } else if (typeof(T) == typeof(double)) { data.Type = 7; } data.Size = (uint)UnsafeUtility.SizeOf<T>(); if (ptr != IntPtr.Zero) { ProfilerUnsafeUtility.SetMarkerMetadata(ptr, 0, metadata, data.Type, (byte)3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void Begin(in T value) { fixed (ProfilerMarkerData* ptr = &data) { void* ptr2 = ptr; fixed (T* ptr3 = &value) { void* ptr4 = ptr3; data.Ptr = ptr4; ProfilerUnsafeUtility.BeginSampleWithMetadata(this.ptr, 1, ptr2); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void End() { ProfilerUnsafeUtility.EndSample(ptr); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AutoScope Auto(T value) { return new AutoScope(in this, value); } } } 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 = true; 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