Decompiled source of PathfindingLagFix v2.2.0
PathfindingLagFix.dll
Decompiled a day 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 HarmonyLib.Public.Patching; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; 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.SceneManagement; 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.2.0")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.2.0.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() { CurrentOptions = presetOption.Value switch { ConfigPreset.OnlyFixes => OnlyFixesPreset, ConfigPreset.Vanilla => VanillaPreset, _ => throw new InvalidOperationException($"Unknown preset {presetOption.Value}"), }; if (distancePathfindingFallbackNodeSelectionOption.Value != 0) { CurrentOptions.DistancePathfindingFallbackNodeSelection = distancePathfindingFallbackNodeSelectionOption.Value; } if (asyncDistancePathfindingMostOptimalDistanceBehaviorOption.Value != 0) { CurrentOptions.AsyncDistancePathfindingMostOptimalDistanceBehavior = asyncDistancePathfindingMostOptimalDistanceBehaviorOption.Value; } Plugin.Instance.Logger.LogInfo((object)string.Format("{0} {1} is using preset {2} with options:", "Zaggy1024.PathfindingLagFix", "2.2.0", presetOption.Value)); Plugin.Instance.Logger.LogInfo((object)string.Format(" {0} = {1} ({2})", "DistancePathfindingFallbackNodeSelection", CurrentOptions.DistancePathfindingFallbackNodeSelection, distancePathfindingFallbackNodeSelectionOption.Value)); Plugin.Instance.Logger.LogInfo((object)string.Format(" {0} = {1} ({2})", "AsyncDistancePathfindingMostOptimalDistanceBehavior", CurrentOptions.AsyncDistancePathfindingMostOptimalDistanceBehavior, asyncDistancePathfindingMostOptimalDistanceBehaviorOption.Value)); } } internal enum DistancePathfindingFallbackNodeSelectionType { UsePreset, BestPathable, Vanilla, DontMove } internal enum AsyncDistancePathfindingMostOptimalDistanceBehaviorType { UsePreset, Set, DontSet } [BepInPlugin("Zaggy1024.PathfindingLagFix", "PathfindingLagFix", "2.2.0")] [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.2.0"; 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)); harmony.PatchAll(typeof(PatchSpringManAI)); harmony.PatchAll(typeof(PatchBlobAI)); harmony.PatchAll(typeof(PatchCaveDwellerAI)); PatchFindMainEntrance.ApplyPatches(harmony); } } } 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, bool calculateDistance = false) { //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_003f: 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_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) 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, calculateDistance); 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 static readonly Vector3 MAX_ORIGIN_EXTENTS = new Vector3(5f, 5f, 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_003c: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Invalid comparison between Unknown and I4 //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Invalid comparison between Unknown and I4 //IL_0105: 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_011f: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Invalid comparison between Unknown and I4 //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017c: 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_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: 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_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Invalid comparison between Unknown and I4 //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_020e: 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_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0278: 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]; NavMeshLocation val3 = ((NavMeshQuery)(ref val2)).MapLocation(Origin, MAX_ORIGIN_EXTENTS, AgentTypeID, AreaMask); if (!((NavMeshQuery)(ref val2)).IsValid(val3)) { Statuses[index] = (PathQueryStatus)int.MinValue; return; } Vector3 val4 = Destinations[index]; Vector3 val5 = new Vector3(1.5f, 1.5f, 1.5f); NavMeshLocation val6 = ((NavMeshQuery)(ref val2)).MapLocation(val4, val5, AgentTypeID, AreaMask); if (!((NavMeshQuery)(ref val2)).IsValid(val6)) { Statuses[index] = (PathQueryStatus)int.MinValue; return; } PathQueryStatus val7 = ((NavMeshQuery)(ref val2)).BeginFindPath(val3, val6, AreaMask, default(NativeArray<float>)); if ((int)PathQueryStatusExtensions.GetResult(val7) == int.MinValue) { Statuses[index] = val7; return; } int num = default(int); while ((int)PathQueryStatusExtensions.GetResult(val7) == 536870912) { val7 = ((NavMeshQuery)(ref val2)).UpdateFindPath(NavMeshLock.RecommendedUpdateFindPathIterationCount, ref num); ((NavMeshReadLocker)(ref val)).Yield(); } int num2 = default(int); val7 = ((NavMeshQuery)(ref val2)).EndFindPath(ref num2); if ((int)PathQueryStatusExtensions.GetResult(val7) != 1073741824) { Statuses[index] = val7; return; } NativeArray<PolygonId> val8 = new NativeArray<PolygonId>(num2, (Allocator)2, (NativeArrayOptions)1); ((NavMeshQuery)(ref val2)).GetPathResult(NativeSlice<PolygonId>.op_Implicit(val8)); int num3 = default(int); val7 = (PathQueryStatus)(NavMeshQueryUtils.FindStraightPath(val2, float3.op_Implicit(Origin), float3.op_Implicit(val4), NativeSlice<PolygonId>.op_Implicit(val8), num2, GetPathBuffer(index), ref num3) | PathQueryStatusExtensions.GetDetail(val7)); ((NavMeshReadLocker)(ref val)).Dispose(); PathSizes[index] = num3; val8.Dispose(); if ((int)PathQueryStatusExtensions.GetResult(val7) != 1073741824) { Statuses[index] = val7; return; } NativeArray<NavMeshLocation> path = GetPath(index); NavMeshLocation val9 = path[path.Length - 1]; Vector3 val10 = ((NavMeshLocation)(ref val9)).position - val4; if (((Vector3)(ref val10)).sqrMagnitude > 2.25f) { Statuses[index] = (PathQueryStatus)(int.MinValue | PathQueryStatusExtensions.GetDetail(val7)); return; } if (CalculateDistance) { float num4 = 0f; for (int i = 1; i < path.Length; i++) { float num5 = num4; val9 = path[i - 1]; Vector3 position = ((NavMeshLocation)(ref val9)).position; val9 = path[i]; num4 = num5 + Vector3.Distance(position, ((NavMeshLocation)(ref val9)).position); } PathDistances[index] = num4; } Statuses[index] = val7; } 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_003d: 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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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 && !Physics.Linecast(position, checkLOSToPosition.Value + Vector3.up * 0.3f, 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 { internal class ILInjector { private const string INVALID = "Injector is invalid"; private List<CodeInstruction> instructions = instructions.ToList(); private ILGenerator generator; private int index; private int matchEnd; public int Index { get { return index; } set { index = value; } } 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) { if (label == default(Label)) { return this; } matchEnd = index; 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 ILInjector 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; return this; } 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 ILInjector DefineLabel(out Label label) { if (generator == null) { throw new InvalidOperationException("No ILGenerator was provided"); } label = generator.DefineLabel(); return this; } public ILInjector AddLabel(out Label label) { DefineLabel(out label); return AddLabel(label); } public ILInjector AddLabel(Label label) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown Instruction = new CodeInstruction(Instruction); 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) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown if (!IsValid) { throw new InvalidOperationException("Injector is invalid"); } List<Label> labels = Instruction.labels; Instruction = new CodeInstruction(Instruction); Instruction.labels.Clear(); this.instructions.InsertRange(index, instructions); Instruction.labels.AddRange(labels); 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); List<Label> labels = instructions[start].labels; instructions.RemoveRange(start, size); index = start; matchEnd = start; instructions[start].labels.AddRange(labels); return this; } public ILInjector ReplaceLastMatch(params CodeInstruction[] replacementInstructions) { if (replacementInstructions.Length == 0) { throw new ArgumentException("Cannot replace a match with an empty array."); } GetLastMatchRange(out var start, out var size); List<Label> labels = instructions[start].labels; instructions.RemoveRange(start, size); instructions.InsertRange(start, replacementInstructions); index = start; matchEnd = start + replacementInstructions.Length; instructions[start].labels.AddRange(labels); 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); } } internal interface ILMatcher { bool Matches(CodeInstruction instruction); ILMatcher CaptureAs(out CodeInstruction variable) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown variable = new CodeInstruction(OpCodes.Nop, (object)null); return new InstructionCapturingMatcher(this, variable); } unsafe ILMatcher CaptureOperandAs<T>(out T operand) where T : unmanaged { operand = default(T); fixed (T* operand2 = &operand) { return new OperandCapturingMatcher<T>(this, operand2); } } ILMatcher Debug() { return new DebuggingMatcher(this); } static ILMatcher Not(ILMatcher matcher) { return new NotMatcher(matcher); } static ILMatcher Opcode(OpCode opcode) { return new OpcodeMatcher(opcode); } static ILMatcher Opcodes(params OpCode[] opcodes) { return new OpcodesMatcher(opcodes); } static ILMatcher OpcodeOperand(OpCode opcode, object operand) { return new OpcodeOperandMatcher(opcode, operand); } static ILMatcher Instruction(CodeInstruction instruction) { return new InstructionMatcher(instruction); } static ILMatcher Ldarg(int? arg = null) { return new LdargMatcher(arg); } static ILMatcher Ldloc(int? loc = null) { return new LdlocMatcher(loc); } static ILMatcher Stloc(int? loc = null) { return new StlocMatcher(loc); } static ILMatcher Ldc(int? value = null) { return new LdcI32Matcher(value); } static ILMatcher LdcF32(float? value = null) { return new LdcF32Matcher(value); } static ILMatcher Branch() { return new BranchMatcher(); } static ILMatcher Ldfld(FieldInfo field, [CallerMemberName] string callerName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { if (field == null) { Plugin.Instance.Logger.LogWarning((object)$"Field passed to ILMatcher.Ldfld() was null at {sourceFilePath}#{sourceLineNumber} ({callerName})"); } return new OpcodeOperandMatcher(OpCodes.Ldfld, field); } static ILMatcher Ldsfld(FieldInfo field, [CallerMemberName] string callerName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { if (field == null) { Plugin.Instance.Logger.LogWarning((object)$"Field passed to ILMatcher.Ldsfld() was null at {sourceFilePath}#{sourceLineNumber} ({callerName})"); } return new OpcodeOperandMatcher(OpCodes.Ldsfld, field); } static ILMatcher Stfld(FieldInfo field, [CallerMemberName] string callerName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { if (field == null) { Plugin.Instance.Logger.LogWarning((object)$"Field passed to ILMatcher.Stfld() was null at {sourceFilePath}#{sourceLineNumber} ({callerName})"); } return new OpcodeOperandMatcher(OpCodes.Stfld, field); } static ILMatcher Stsfld(FieldInfo field, [CallerMemberName] string callerName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { if (field == null) { Plugin.Instance.Logger.LogWarning((object)$"Field passed to ILMatcher.Stsfld() was null at {sourceFilePath}#{sourceLineNumber} ({callerName})"); } return new OpcodeOperandMatcher(OpCodes.Stsfld, field); } static ILMatcher Callvirt(MethodBase method, [CallerMemberName] string callerName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { if (method == null) { Plugin.Instance.Logger.LogWarning((object)$"Method passed to ILMatcher.Callvirt() was null at {sourceFilePath}#{sourceLineNumber} ({callerName})"); } return OpcodeOperand(OpCodes.Callvirt, method); } static ILMatcher Call(MethodBase method, [CallerMemberName] string callerName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { if (method == null) { Plugin.Instance.Logger.LogWarning((object)$"Method passed to ILMatcher.Call() was null at {sourceFilePath}#{sourceLineNumber} ({callerName})"); } return OpcodeOperand(OpCodes.Call, method); } static ILMatcher Predicate(Func<CodeInstruction, bool> predicate) { return new PredicateMatcher(predicate); } static ILMatcher Predicate(Func<FieldInfo, bool> predicate) { return new PredicateMatcher((CodeInstruction insn) => insn.operand is FieldInfo arg && predicate(arg)); } } internal 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); } } internal 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; } } internal 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); } } internal 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; } } internal 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; } } internal 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; } } internal 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; } } internal 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; } } internal class LdcI32Matcher : ILMatcher { private readonly int? value; public LdcI32Matcher(int? value) { this.value = value; base..ctor(); } public bool Matches(CodeInstruction instruction) { if (!value.HasValue) { return instruction.GetLdcI32().HasValue; } return instruction.GetLdcI32() == value; } } internal class LdcF32Matcher : ILMatcher { private readonly float? value; public LdcF32Matcher(float? value) { this.value = value; base..ctor(); } public bool Matches(CodeInstruction instruction) { if (instruction.opcode == OpCodes.Ldc_R4) { if (value.HasValue) { return (float)instruction.operand == value.Value; } return true; } return false; } } internal class BranchMatcher : ILMatcher { public bool Matches(CodeInstruction instruction) { Label? label = default(Label?); return CodeInstructionExtensions.Branches(instruction, ref label); } } internal 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 class InstructionCapturingMatcher : ILMatcher { private readonly ILMatcher matcher; private readonly CodeInstruction variable; public InstructionCapturingMatcher(ILMatcher matcher, CodeInstruction variable) { this.matcher = matcher; this.variable = variable; base..ctor(); } public bool Matches(CodeInstruction instruction) { bool num = matcher.Matches(instruction); if (num) { variable.opcode = instruction.opcode; variable.operand = instruction.operand; CodeInstruction obj = variable; List<ExceptionBlock> blocks = instruction.blocks; List<ExceptionBlock> list = new List<ExceptionBlock>(blocks.Count); list.AddRange(blocks); obj.blocks = list; CodeInstruction obj2 = variable; List<Label> labels = instruction.labels; List<Label> list2 = new List<Label>(labels.Count); list2.AddRange(labels); obj2.labels = list2; } return num; } } internal class OperandCapturingMatcher<T> : ILMatcher where T : unmanaged { private readonly ILMatcher matcher; private unsafe readonly T* operand; public unsafe OperandCapturingMatcher(ILMatcher matcher, T* operand) { this.matcher = matcher; this.operand = operand; base..ctor(); } public unsafe bool Matches(CodeInstruction instruction) { bool num = matcher.Matches(instruction); if (num) { *operand = (T)instruction.operand; } return num; } } internal class DebuggingMatcher : ILMatcher { private readonly ILMatcher matcher; public DebuggingMatcher(ILMatcher matcher) { this.matcher = matcher; base..ctor(); } public bool Matches(CodeInstruction instruction) { bool num = matcher.Matches(instruction); if (num) { Plugin.Instance.Logger.LogInfo((object)$"{matcher} matched {instruction}"); } return num; } } 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 || instruction.opcode == OpCodes.Newobj) { MethodBase obj = (MethodBase)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 || instruction.opcode == OpCodes.Newobj) { if (instruction.operand is MethodInfo methodInfo && methodInfo.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(EnemyAI))] internal class PatchAddProfilerMarkers { private static readonly Dictionary<MethodInfo, List<int>> callOffsets = new Dictionary<MethodInfo, List<int>>(); private static readonly Dictionary<string, ProfilerMarker> markers = new Dictionary<string, ProfilerMarker>(); private static string currentMarkerName = ""; internal static void ApplyPatches(Harmony harmony) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Expected O, but got Unknown Type[] types = typeof(EnemyAI).Assembly.GetTypes(); foreach (Type type in types) { if (type != typeof(EnemyAI) && !type.IsSubclassOf(typeof(EnemyAI))) { continue; } foreach (MethodInfo runtimeMethod in type.GetRuntimeMethods()) { if (runtimeMethod.DeclaringType != type || !MethodBaseExtensions.HasMethodBody((MethodBase)runtimeMethod)) { continue; } Collection<Instruction> instructions = PatchManager.GetMethodPatcher((MethodBase)runtimeMethod).CopyOriginal().Definition.Body.Instructions; bool flag = false; Enumerator<Instruction> enumerator2 = instructions.GetEnumerator(); try { while (enumerator2.MoveNext()) { Instruction current2 = enumerator2.Current; if (current2.OpCode != OpCodes.Call && current2.OpCode != OpCodes.Callvirt) { continue; } object operand = current2.Operand; MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null); if (val != null && !(((MemberReference)((MemberReference)val).DeclaringType).Name != "EnemyAI")) { switch (((MemberReference)val).Name) { case "PathIsIntersectedByLineOfSight": case "ChooseFarthestNodeFromPosition": case "ChooseClosestNodeToPosition": flag = true; GetCallOffsets(runtimeMethod).Add(current2.Offset); break; } } } } finally { ((IDisposable)enumerator2).Dispose(); } if (flag) { new PatchProcessor(harmony, (MethodBase)runtimeMethod).AddTranspiler(new HarmonyMethod(Reflection.GetMethod(typeof(PatchAddProfilerMarkers), "CallerTranspiler", BindingFlags.Static | BindingFlags.NonPublic, new Type[3] { typeof(IEnumerable<CodeInstruction>), typeof(ILGenerator), typeof(MethodBase) })) { priority = 800 }).Patch(); } } } harmony.PatchAll(typeof(PatchAddProfilerMarkers)); } private static List<int> GetCallOffsets(MethodInfo method) { if (callOffsets.TryGetValue(method, out var value)) { return value; } value = new List<int>(); callOffsets[method] = value; return value; } private static IEnumerable<CodeInstruction> CallerTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator, MethodBase method) { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown if (!(method is MethodInfo method2)) { return instructions; } Plugin.Instance.Logger.LogInfo((object)("Begin caller transpiler for " + method.DeclaringType.FullName + "::" + method.Name)); ILInjector iLInjector = new ILInjector(instructions); int num = 0; while (true) { iLInjector.Find(ILMatcher.Predicate((CodeInstruction insn) => CodeInstructionExtensions.Calls(insn, Reflection.m_EnemyAI_PathIsIntersectedByLineOfSight) || CodeInstructionExtensions.Calls(insn, Reflection.m_EnemyAI_ChooseFarthestNodeFromPosition) || CodeInstructionExtensions.Calls(insn, Reflection.m_EnemyAI_ChooseClosestNodeToPosition))); int index = iLInjector.Index; if (!iLInjector.IsValid) { break; } MethodBase methodBase = (MethodBase)iLInjector.Instruction.operand; iLInjector.GoToPush(methodBase.GetParameters().Length + 1).InsertAfterBranch(new CodeInstruction(OpCodes.Ldstr, (object)$"{method.Name}:IL_{GetCallOffsets(method2)[num]:X} -> {methodBase.Name}"), new CodeInstruction(OpCodes.Stsfld, (object)typeof(PatchAddProfilerMarkers).GetField("currentMarkerName", BindingFlags.Static | BindingFlags.NonPublic))); iLInjector.Index = index + 3; num++; } return iLInjector.ReleaseInstructions(); } private static ProfilerMarker GetMarker(string name) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (markers.TryGetValue(name, out var value)) { return value; } Plugin.Instance.Logger.LogInfo((object)$"Adding a marker named '{name}', up to {markers.Count + 1} total"); ((ProfilerMarker)(ref value))..ctor(name); markers[name] = value; return value; } [HarmonyTranspiler] [HarmonyPatch("PathIsIntersectedByLineOfSight")] [HarmonyPatch("ChooseFarthestNodeFromPosition")] [HarmonyPatch("ChooseClosestNodeToPosition")] private static IEnumerable<CodeInstruction> CalleeTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator, MethodBase method) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown LocalBuilder localBuilder = generator.DeclareLocal(typeof(ProfilerMarker)); ILInjector iLInjector = new ILInjector(instructions).Insert(new CodeInstruction(OpCodes.Ldsfld, (object)typeof(PatchAddProfilerMarkers).GetField("currentMarkerName", BindingFlags.Static | BindingFlags.NonPublic)), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchAddProfilerMarkers), "GetMarker", BindingFlags.Static | BindingFlags.NonPublic, new Type[1] { typeof(string) })), new CodeInstruction(OpCodes.Stloc, (object)localBuilder), new CodeInstruction(OpCodes.Ldloca, (object)localBuilder), new CodeInstruction(OpCodes.Call, (object)typeof(ProfilerMarker).GetMethod("Begin", Array.Empty<Type>()))); while (true) { iLInjector.Find(ILMatcher.Opcode(OpCodes.Ret)); if (!iLInjector.IsValid) { break; } iLInjector.Insert(new CodeInstruction(OpCodes.Ldloca, (object)localBuilder), new CodeInstruction(OpCodes.Call, (object)typeof(ProfilerMarker).GetMethod("End"))).GoToMatchEnd(); } return iLInjector.ReleaseInstructions(); } } [HarmonyPatch(typeof(BlobAI))] internal static class PatchBlobAI { private struct BlobUpdateData { internal int index; internal RaycastHit[] hits; internal Vector3[] navmeshPositions; public BlobUpdateData() { index = 0; hits = (RaycastHit[])(object)new RaycastHit[8]; navmeshPositions = (Vector3[])(object)new Vector3[8]; } } private static bool limitBlobUpdates = true; private static IDMap<BlobUpdateData> blobUpdateIndices = new IDMap<BlobUpdateData>(() => new BlobUpdateData(), 0); private static int GetBlobUpdateIndex(BlobAI blob) { if (!limitBlobUpdates) { return -1; } ref int index = ref blobUpdateIndices[((EnemyAI)blob).thisEnemyIndex].index; int result = index; index = (index + 1) % blob.SlimeRaycastTargets.Length; return result; } private static void SetBlobRayHit(BlobAI blob, int index) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) blobUpdateIndices[((EnemyAI)blob).thisEnemyIndex].hits[index] = blob.slimeRayHit; } private static RaycastHit GetBlobRayHit(BlobAI blob, int index) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) return blobUpdateIndices[((EnemyAI)blob).thisEnemyIndex].hits[index]; } private static Vector3 GetBlobNavMeshPosition(BlobAI blob, int index) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) return blobUpdateIndices[((EnemyAI)blob).thisEnemyIndex].navmeshPositions[index]; } private static void SetBlobNavMeshPosition(BlobAI blob, int index, Vector3 position) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) blobUpdateIndices[((EnemyAI)blob).thisEnemyIndex].navmeshPositions[index] = position; } [HarmonyTranspiler] [HarmonyPatch("Update")] private static IEnumerable<CodeInstruction> UpdateTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Expected O, but got Unknown //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Expected O, but got Unknown //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Expected O, but got Unknown //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Expected O, but got Unknown //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Expected O, but got Unknown //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Expected O, but got Unknown //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Expected O, but got Unknown //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Expected O, but got Unknown //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Expected O, but got Unknown //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Expected O, but got Unknown //IL_03c5: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Expected O, but got Unknown //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Expected O, but got Unknown //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Expected O, but got Unknown //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_040f: Expected O, but got Unknown //IL_0450: Unknown result type (might be due to invalid IL or missing references) //IL_0456: Expected O, but got Unknown //IL_045f: Unknown result type (might be due to invalid IL or missing references) //IL_0465: Expected O, but got Unknown //IL_046e: Unknown result type (might be due to invalid IL or missing references) //IL_0487: Expected O, but got Unknown //IL_0492: Unknown result type (might be due to invalid IL or missing references) //IL_0498: Expected O, but got Unknown //IL_04b4: Unknown result type (might be due to invalid IL or missing references) //IL_04ba: Expected O, but got Unknown //IL_04c3: Unknown result type (might be due to invalid IL or missing references) //IL_04c9: Expected O, but got Unknown //IL_04d8: Unknown result type (might be due to invalid IL or missing references) //IL_04de: Expected O, but got Unknown //IL_05ae: Unknown result type (might be due to invalid IL or missing references) //IL_05b4: Expected O, but got Unknown //IL_05b7: Unknown result type (might be due to invalid IL or missing references) //IL_05bd: Expected O, but got Unknown //IL_05c6: Unknown result type (might be due to invalid IL or missing references) //IL_05cc: Expected O, but got Unknown //IL_0616: Unknown result type (might be due to invalid IL or missing references) //IL_061c: Expected O, but got Unknown //IL_063b: Unknown result type (might be due to invalid IL or missing references) //IL_0641: Expected O, but got Unknown //IL_0682: Unknown result type (might be due to invalid IL or missing references) //IL_0688: Expected O, but got Unknown //IL_0695: Unknown result type (might be due to invalid IL or missing references) //IL_069b: Expected O, but got Unknown //IL_06a9: Unknown result type (might be due to invalid IL or missing references) //IL_06af: Expected O, but got Unknown LocalBuilder localBuilder = generator.DeclareLocal(typeof(int)); CodeInstruction variable; ILInjector iLInjector = new ILInjector(instructions).Insert(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchBlobAI), "GetBlobUpdateIndex", BindingFlags.Static | BindingFlags.NonPublic, new Type[1] { typeof(BlobAI) })), new CodeInstruction(OpCodes.Stloc, (object)localBuilder)).Find(ILMatcher.Ldarg(0), ILMatcher.Ldloc(), ILMatcher.Ldarg(0), ILMatcher.Ldfld(typeof(BlobAI).GetField("SlimeBones"), "UpdateTranspiler", "./Patches/PatchBlobAI.cs", 102), ILMatcher.Ldloc().CaptureAs(out variable), ILMatcher.Opcode(OpCodes.Ldelem_Ref), ILMatcher.Callvirt(Reflection.m_Component_get_transform, "UpdateTranspiler", "./Patches/PatchBlobAI.cs", 105), ILMatcher.Callvirt(Reflection.m_Transform_get_position, "UpdateTranspiler", "./Patches/PatchBlobAI.cs", 106), ILMatcher.Call(typeof(Vector3).GetMethod("Distance", new Type[2] { typeof(Vector3), typeof(Vector3) }), "UpdateTranspiler", "./Patches/PatchBlobAI.cs", 107), ILMatcher.Call(Reflection.GetMethod(typeof(BlobAI), "RaycastCollisionWithPlayers", BindingFlags.Instance | BindingFlags.NonPublic, new Type[1] { typeof(float) }), "UpdateTranspiler", "./Patches/PatchBlobAI.cs", 108)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the call to RaycastCollisionWithPlayers in BlobAI.Update()."); return instructions; } LocalBuilder localBuilder2 = generator.DeclareLocal(typeof(bool)); Label label = generator.DefineLabel(); iLInjector.Insert(new CodeInstruction(OpCodes.Ldloc, (object)localBuilder), new CodeInstruction(OpCodes.Ldc_I4_M1, (object)null), new CodeInstruction(OpCodes.Ceq, (object)null), new CodeInstruction(OpCodes.Ldloc, (object)localBuilder), variable, new CodeInstruction(OpCodes.Ceq, (object)null), new CodeInstruction(OpCodes.Or, (object)null), new CodeInstruction(OpCodes.Stloc, (object)localBuilder2), new CodeInstruction(OpCodes.Ldloc, (object)localBuilder2), new CodeInstruction(OpCodes.Brfalse, (object)label)).Find(ILMatcher.Call(typeof(Physics).GetMethod("Raycast", new Type[5] { typeof(Ray), typeof(RaycastHit).MakeByRefType(), typeof(float), typeof(int), typeof(QueryTriggerInteraction) }), "UpdateTranspiler", "./Patches/PatchBlobAI.cs", 137), ILMatcher.Opcode(OpCodes.Brfalse).CaptureOperandAs<Label>(out var operand)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the raycast in BlobAI.Update()."); return instructions; } Label label2 = generator.DefineLabel(); FieldInfo field = typeof(BlobAI).GetField("slimeRayHit", BindingFlags.Instance | BindingFlags.NonPublic); iLInjector.SetRelativeInstruction(1, new CodeInstruction(OpCodes.Pop, (object)null)).GoToMatchEnd().Insert(new CodeInstruction(OpCodes.Ldarg_0, (object)null), variable, new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchBlobAI), "SetBlobRayHit", BindingFlags.Static | BindingFlags.NonPublic, new Type[2] { typeof(BlobAI), typeof(int) })), new CodeInstruction(OpCodes.Br, (object)label2), CodeInstructionExtensions.WithLabels(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new Label[1] { label }), new CodeInstruction(OpCodes.Ldarg_0, (object)null), variable, new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchBlobAI), "GetBlobRayHit", BindingFlags.Static | BindingFlags.NonPublic, new Type[2] { typeof(BlobAI), typeof(int) })), new CodeInstruction(OpCodes.Stfld, (object)field), CodeInstructionExtensions.WithLabels(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new Label[1] { label2 }), new CodeInstruction(OpCodes.Ldflda, (object)field), new CodeInstruction(OpCodes.Call, (object)typeof(RaycastHit).GetMethod("get_colliderInstanceID")), new CodeInstruction(OpCodes.Ldc_I4_0, (object)null), new CodeInstruction(OpCodes.Beq_S, (object)operand)) .Find(ILMatcher.Callvirt(typeof(RoundManager).GetMethod("GetNavMeshPosition", new Type[4] { typeof(Vector3), typeof(NavMeshHit), typeof(float), typeof(int) }), "UpdateTranspiler", "./Patches/PatchBlobAI.cs", 178), ILMatcher.Stloc().CaptureAs(out var variable2)); if (!iLInjector.IsValid) { Plugin.Instance.Logger.LogError((object)"Failed to find the navmesh sample in BlobAI.Update()."); return instructions; } Label label3 = generator.DefineLabel(); return iLInjector.GoToMatchEnd().AddLabel(label3).InsertInPlace(new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(variable), new CodeInstruction(variable2.StlocToLdloc()), new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchBlobAI), "SetBlobNavMeshPosition", BindingFlags.Static | BindingFlags.NonPublic, new Type[3] { typeof(BlobAI), typeof(int), typeof(Vector3) }))) .Back(2) .GoToPush(5) .InsertAfterBranch(new CodeInstruction(OpCodes.Ldarg_0, (object)null), variable, new CodeInstruction(OpCodes.Call, (object)Reflection.GetMethod(typeof(PatchBlobAI), "GetBlobNavMeshPosition", BindingFlags.Static | BindingFlags.NonPublic, new Type[2] { typeof(BlobAI), typeof(int) })), variable2, new CodeInstruction(OpCodes.Ldloc, (object)localBuilder2), new CodeInstruction(OpCodes.Brfalse_S, (object)label3)) .ReleaseInstructions(); } } [HarmonyPatch(typeof(CaveDwellerAI))] internal static class PatchCaveDwellerAI { private class StuckCheckStatus { internal FindPathsToNodesJob job; internal JobHandle jobHandle; internal Vector3[] nodePositions; internal bool jobStarted; internal bool IsStuck() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Invalid comparison between Unknown and I4 if (!jobStarted) { Plugin.Instance.Logger.LogError((object)"Cave dweller stuck check jobs have not been started before checking their results."); return false; } for (int i = 0; i < nodePositions.Length; i++) { PathQueryStatus result = PathQueryStat