diff --git a/project/Aki.Core/Patches/BattlEyePatch.cs b/project/Aki.Core/Patches/BattlEyePatch.cs index 3fc27b9..8be7e26 100644 --- a/project/Aki.Core/Patches/BattlEyePatch.cs +++ b/project/Aki.Core/Patches/BattlEyePatch.cs @@ -1,9 +1,8 @@ using Aki.Core.Utils; using Aki.Reflection.Patching; -using Aki.Reflection.Utils; -using System.Linq; using System.Reflection; using System.Threading.Tasks; +using HarmonyLib; namespace Aki.Core.Patches { @@ -11,11 +10,7 @@ namespace Aki.Core.Patches { protected override MethodBase GetTargetMethod() { - var methodName = "RunValidation"; - var flags = BindingFlags.Public | BindingFlags.Instance; - - return PatchConstants.EftTypes.Single(x => x.GetMethod(methodName, flags) != null) - .GetMethod(methodName, flags); + return AccessTools.Method(typeof(BattleeyePatchClass), nameof(BattleeyePatchClass.RunValidation)); } [PatchPrefix] diff --git a/project/Aki.Core/Patches/ConsistencyMultiPatch.cs b/project/Aki.Core/Patches/ConsistencyMultiPatch.cs index 7a71d8b..ad24902 100644 --- a/project/Aki.Core/Patches/ConsistencyMultiPatch.cs +++ b/project/Aki.Core/Patches/ConsistencyMultiPatch.cs @@ -12,8 +12,8 @@ namespace Aki.Core.Patches { protected override MethodBase GetTargetMethod() { - return PatchConstants.FilesCheckerTypes.Single(x => x.Name == "ConsistencyController") - .GetMethods().Single(x => x.Name == "EnsureConsistency" && x.ReturnType == typeof(Task)); + return PatchConstants.FilesCheckerTypes.SingleCustom(x => x.Name == "ConsistencyController") + .GetMethods().SingleCustom(x => x.Name == "EnsureConsistency" && x.ReturnType == typeof(Task)); } [PatchPrefix] diff --git a/project/Aki.Core/Patches/ConsistencySinglePatch.cs b/project/Aki.Core/Patches/ConsistencySinglePatch.cs index 96447b9..fb0172b 100644 --- a/project/Aki.Core/Patches/ConsistencySinglePatch.cs +++ b/project/Aki.Core/Patches/ConsistencySinglePatch.cs @@ -12,8 +12,8 @@ namespace Aki.Core.Patches { protected override MethodBase GetTargetMethod() { - return PatchConstants.FilesCheckerTypes.Single(x => x.Name == "ConsistencyController") - .GetMethods().Single(x => x.Name == "EnsureConsistencySingle" && x.ReturnType == typeof(Task)); + return PatchConstants.FilesCheckerTypes.SingleCustom(x => x.Name == "ConsistencyController") + .GetMethods().SingleCustom(x => x.Name == "EnsureConsistencySingle" && x.ReturnType == typeof(Task)); } [PatchPrefix] diff --git a/project/Aki.Core/Patches/DataHandlerDebugPatch.cs b/project/Aki.Core/Patches/DataHandlerDebugPatch.cs deleted file mode 100644 index d23c2a3..0000000 --- a/project/Aki.Core/Patches/DataHandlerDebugPatch.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using Aki.Core.Models; -using System.Threading.Tasks; -using Aki.Reflection.Patching; -using Aki.Reflection.Utils; -using FilesChecker; -using HarmonyLib; -using System; - -namespace Aki.Core.Patches -{ - public class DataHandlerDebugPatch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return PatchConstants.EftTypes - .Single(t => t.Name == "DataHandler") - .GetMethod("method_5", BindingFlags.Instance | BindingFlags.NonPublic); - } - - [PatchPostfix] - private static void PatchPrefix(ref string __result) - { - Console.WriteLine($"response json: ${__result}"); - } - } -} \ No newline at end of file diff --git a/project/Aki.Core/Patches/SslCertificatePatch.cs b/project/Aki.Core/Patches/SslCertificatePatch.cs index 972ca66..83893f3 100644 --- a/project/Aki.Core/Patches/SslCertificatePatch.cs +++ b/project/Aki.Core/Patches/SslCertificatePatch.cs @@ -1,25 +1,23 @@ -using System.Linq; using System.Reflection; -using UnityEngine.Networking; +using System.Security.Cryptography.X509Certificates; using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using Aki.Core.Utils; +using HarmonyLib; namespace Aki.Core.Patches { public class SslCertificatePatch : ModulePatch { protected override MethodBase GetTargetMethod() - { - return PatchConstants.EftTypes.Single(x => x.BaseType == typeof(CertificateHandler)) - .GetMethod("ValidateCertificate", PatchConstants.PrivateFlags); + { + return AccessTools.Method(typeof(SslCertPatchClass), nameof(SslCertPatchClass.ValidateCertificate), new[] { typeof(X509Certificate) }); } [PatchPrefix] private static bool PatchPrefix(ref bool __result) { __result = ValidationUtil.Validate(); - return false; // Skip origial + return false; // Skip original } } } diff --git a/project/Aki.Core/Patches/TransportPrefixPatch.cs b/project/Aki.Core/Patches/TransportPrefixPatch.cs index 2d2355a..fc0e19c 100644 --- a/project/Aki.Core/Patches/TransportPrefixPatch.cs +++ b/project/Aki.Core/Patches/TransportPrefixPatch.cs @@ -35,18 +35,17 @@ namespace Aki.Core.Patches protected override MethodBase GetTargetMethod() { - return PatchConstants.EftTypes.Single(t => t.GetMethods().Any(m => m.Name == "CreateFromLegacyParams")) + return PatchConstants.EftTypes.SingleCustom(t => t.GetMethods().Any(m => m.Name == "CreateFromLegacyParams")) .GetMethod("CreateFromLegacyParams", BindingFlags.Static | BindingFlags.Public); } [PatchPrefix] private static bool PatchPrefix(ref LegacyParamsStruct legacyParams) { - //Console.WriteLine($"Original url {legacyParams.Url}"); legacyParams.Url = legacyParams.Url .Replace("https://", "") .Replace("http://", ""); - //Console.WriteLine($"Edited url {legacyParams.Url}"); + return true; // do original method after } diff --git a/project/Aki.Core/Patches/WebSocketPatch.cs b/project/Aki.Core/Patches/WebSocketPatch.cs index 856d6f9..aac0b1e 100644 --- a/project/Aki.Core/Patches/WebSocketPatch.cs +++ b/project/Aki.Core/Patches/WebSocketPatch.cs @@ -1,7 +1,6 @@ using Aki.Reflection.Patching; using Aki.Reflection.Utils; using System; -using System.Linq; using System.Reflection; namespace Aki.Core.Patches @@ -10,15 +9,18 @@ namespace Aki.Core.Patches { protected override MethodBase GetTargetMethod() { - var targetInterface = PatchConstants.EftTypes.Single(x => x == typeof(IConnectionHandler) && x.IsInterface); - var typeThatMatches = PatchConstants.EftTypes.Single(x => targetInterface.IsAssignableFrom(x) && x.IsAbstract && !x.IsInterface); - return typeThatMatches.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Single(x => x.ReturnType == typeof(Uri)); + var targetInterface = PatchConstants.EftTypes.SingleCustom(x => x == typeof(IConnectionHandler) && x.IsInterface); + var typeThatMatches = PatchConstants.EftTypes.SingleCustom(x => targetInterface.IsAssignableFrom(x) && x.IsAbstract && !x.IsInterface); + + return typeThatMatches.GetMethods(BindingFlags.Public | BindingFlags.Instance).SingleCustom(x => x.ReturnType == typeof(Uri)); } + // This is a pass through postfix and behaves a little differently than usual + // https://harmony.pardeike.net/articles/patching-postfix.html#pass-through-postfixes [PatchPostfix] - private static Uri PatchPostfix(Uri __instance) + private static Uri PatchPostfix(Uri __result) { - return new Uri(__instance.ToString().Replace("wss:", "ws:")); + return new Uri(__result.ToString().Replace("wss:", "ws:")); } } } diff --git a/project/Aki.Custom/AkiCustomPlugin.cs b/project/Aki.Custom/AkiCustomPlugin.cs index ca0a108..5822310 100644 --- a/project/Aki.Custom/AkiCustomPlugin.cs +++ b/project/Aki.Custom/AkiCustomPlugin.cs @@ -3,7 +3,6 @@ using Aki.Common; using Aki.Custom.Airdrops.Patches; using Aki.Custom.Patches; using Aki.Custom.Utils; -using Aki.SinglePlayer.Patches.ScavMode; using BepInEx; namespace Aki.Custom @@ -29,7 +28,7 @@ namespace Aki.Custom // Fixed in live, no need for patch //new RaidSettingsWindowPatch().Enable(); new OfflineRaidSettingsMenuPatch().Enable(); - new SessionIdPatch().Enable(); + // new SessionIdPatch().Enable(); new VersionLabelPatch().Enable(); new IsEnemyPatch().Enable(); new LocationLootCacheBustingPatch().Enable(); diff --git a/project/Aki.Custom/Patches/AddEnemyPatch.cs b/project/Aki.Custom/Patches/AddEnemyPatch.cs deleted file mode 100644 index 7fbd8e9..0000000 --- a/project/Aki.Custom/Patches/AddEnemyPatch.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Aki.Reflection.Patching; -using EFT; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Aki.Custom.Patches -{ - /// - /// If a bot being added has an ID found in list_1, it means its trying to add itself to its enemy list - /// Dont add bot to enemy list if its in list_1 and skip the rest of the AddEnemy() function - /// - public class AddSelfAsEnemyPatch : ModulePatch - { - private static readonly string methodName = "AddEnemy"; - - protected override MethodBase GetTargetMethod() - { - return typeof(BotZoneGroupsDictionary).GetMethod(methodName); - } - - [PatchPrefix] - private static bool PatchPrefix(BotZoneGroupsDictionary __instance, IPlayer person) - { - var botOwners = (List)__instance.GetType().GetField("list_1", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance); - if (botOwners.Any(x => x.Id == person.Id)) - { - return false; - } - - return true; - } - } -} diff --git a/project/Aki.Custom/Patches/AddEnemyToAllGroupsInBotZonePatch.cs b/project/Aki.Custom/Patches/AddEnemyToAllGroupsInBotZonePatch.cs index 304d18f..d30c958 100644 --- a/project/Aki.Custom/Patches/AddEnemyToAllGroupsInBotZonePatch.cs +++ b/project/Aki.Custom/Patches/AddEnemyToAllGroupsInBotZonePatch.cs @@ -1,38 +1,15 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT; -using System; -using System.Linq; using System.Reflection; +using HarmonyLib; namespace Aki.Custom.Patches { public class AddEnemyToAllGroupsInBotZonePatch : ModulePatch { - private static Type _targetType; - private const string methodName = "AddEnemyToAllGroupsInBotZone"; - - public AddEnemyToAllGroupsInBotZonePatch() - { - _targetType = PatchConstants.EftTypes.Single(IsTargetType); - } - - private bool IsTargetType(Type type) - { - if (type.Name == nameof(BotsController) && type.GetMethod(methodName) != null) - { - return true; - } - - return false; - } - protected override MethodBase GetTargetMethod() { - Logger.LogDebug($"{this.GetType().Name} Type: {_targetType.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {methodName}"); - - return _targetType.GetMethod(methodName); + return AccessTools.Method(typeof(BotsController), nameof(BotsController.AddEnemyToAllGroupsInBotZone)); } /// diff --git a/project/Aki.Custom/Patches/AddSptBotSettingsPatch.cs b/project/Aki.Custom/Patches/AddSptBotSettingsPatch.cs index d5b4669..a261a48 100644 --- a/project/Aki.Custom/Patches/AddSptBotSettingsPatch.cs +++ b/project/Aki.Custom/Patches/AddSptBotSettingsPatch.cs @@ -3,6 +3,7 @@ using System.Reflection; using Aki.PrePatch; using Aki.Reflection.Patching; using EFT; +using HarmonyLib; namespace Aki.Custom.Patches { @@ -10,7 +11,7 @@ namespace Aki.Custom.Patches { protected override MethodBase GetTargetMethod() { - return typeof(BotSettingsRepoClass).GetMethod("Init"); + return AccessTools.Method(typeof(BotSettingsRepoClass), nameof(BotSettingsRepoClass.Init)); } [PatchPrefix] diff --git a/project/Aki.Custom/Patches/BossSpawnChancePatch.cs b/project/Aki.Custom/Patches/BossSpawnChancePatch.cs index 3fb2658..14b64b4 100644 --- a/project/Aki.Custom/Patches/BossSpawnChancePatch.cs +++ b/project/Aki.Custom/Patches/BossSpawnChancePatch.cs @@ -16,11 +16,11 @@ namespace Aki.Custom.Patches { var desiredType = PatchConstants.LocalGameType; var desiredMethod = desiredType - .GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly) - .SingleOrDefault(m => IsTargetMethod(m)); + .GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) + .SingleOrDefault(IsTargetMethod); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod.Name}"); + Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name ?? "NOT FOUND"}"); return desiredMethod; } diff --git a/project/Aki.Custom/Patches/BotDifficultyPatch.cs b/project/Aki.Custom/Patches/BotDifficultyPatch.cs index a684ed6..2f5ebe3 100644 --- a/project/Aki.Custom/Patches/BotDifficultyPatch.cs +++ b/project/Aki.Custom/Patches/BotDifficultyPatch.cs @@ -3,7 +3,6 @@ using Aki.Reflection.Patching; using Aki.Reflection.Utils; using EFT; using EFT.UI; -using System.Linq; using System.Reflection; namespace Aki.Custom.Patches @@ -15,7 +14,7 @@ namespace Aki.Custom.Patches var methodName = "LoadDifficultyStringInternal"; var flags = BindingFlags.Public | BindingFlags.Static; - return PatchConstants.EftTypes.Single(x => x.GetMethod(methodName, flags) != null) + return PatchConstants.EftTypes.SingleCustom(x => x.GetMethod(methodName, flags) != null) .GetMethod(methodName, flags); } diff --git a/project/Aki.Custom/Patches/BotEnemyTargetPatch.cs b/project/Aki.Custom/Patches/BotEnemyTargetPatch.cs index 6524d70..2d02a66 100644 --- a/project/Aki.Custom/Patches/BotEnemyTargetPatch.cs +++ b/project/Aki.Custom/Patches/BotEnemyTargetPatch.cs @@ -1,36 +1,15 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT; -using System; -using System.Linq; using System.Reflection; +using HarmonyLib; namespace Aki.Custom.Patches { public class BotEnemyTargetPatch : ModulePatch { - private static Type _targetType; - private static readonly string methodName = "AddEnemyToAllGroupsInBotZone"; - - public BotEnemyTargetPatch() - { - _targetType = PatchConstants.EftTypes.Single(IsTargetType); - } - - private bool IsTargetType(Type type) - { - if (type.Name == nameof(BotsController) && type.GetMethod(methodName) != null) - { - Logger.LogInfo($"{methodName}: {type.FullName}"); - return true; - } - - return false; - } - protected override MethodBase GetTargetMethod() { - return _targetType.GetMethod(methodName); + return AccessTools.Method(typeof(BotsController), nameof(BotsController.AddEnemyToAllGroupsInBotZone)); } /// diff --git a/project/Aki.Custom/Patches/BotSelfEnemyPatch.cs b/project/Aki.Custom/Patches/BotSelfEnemyPatch.cs index cc7ca9e..179f24c 100644 --- a/project/Aki.Custom/Patches/BotSelfEnemyPatch.cs +++ b/project/Aki.Custom/Patches/BotSelfEnemyPatch.cs @@ -1,6 +1,7 @@ using Aki.Reflection.Patching; using EFT; using System.Reflection; +using HarmonyLib; namespace Aki.Custom.Patches { @@ -9,11 +10,9 @@ namespace Aki.Custom.Patches /// internal class BotSelfEnemyPatch : ModulePatch { - private static readonly string methodName = "PreActivate"; - protected override MethodBase GetTargetMethod() { - return typeof(BotOwner).GetMethod(methodName); + return AccessTools.Method(typeof(BotOwner), nameof(BotOwner.PreActivate)); } [PatchPrefix] diff --git a/project/Aki.Custom/Patches/CheckAndAddEnemyPatch.cs b/project/Aki.Custom/Patches/CheckAndAddEnemyPatch.cs index 54dd730..f1ae50d 100644 --- a/project/Aki.Custom/Patches/CheckAndAddEnemyPatch.cs +++ b/project/Aki.Custom/Patches/CheckAndAddEnemyPatch.cs @@ -1,38 +1,15 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT; -using System; -using System.Linq; using System.Reflection; +using HarmonyLib; namespace Aki.Custom.Patches { public class CheckAndAddEnemyPatch : ModulePatch { - private static Type _targetType; - private readonly string _targetMethodName = "CheckAndAddEnemy"; - - /// - /// BotGroupClass.CheckAndAddEnemy() - /// - public CheckAndAddEnemyPatch() - { - _targetType = PatchConstants.EftTypes.Single(IsTargetType); - } - - private bool IsTargetType(Type type) - { - if (type.GetMethod("AddEnemy") != null && type.GetMethod("AddEnemyGroupIfAllowed") != null) - { - return true; - } - - return false; - } - protected override MethodBase GetTargetMethod() { - return _targetType.GetMethod(_targetMethodName); + return AccessTools.Method(typeof(BotsGroup), nameof(BotsGroup.CheckAndAddEnemy)); } /// diff --git a/project/Aki.Custom/Patches/CoreDifficultyPatch.cs b/project/Aki.Custom/Patches/CoreDifficultyPatch.cs index 379c6d4..4dc2d70 100644 --- a/project/Aki.Custom/Patches/CoreDifficultyPatch.cs +++ b/project/Aki.Custom/Patches/CoreDifficultyPatch.cs @@ -1,7 +1,6 @@ using Aki.Reflection.Patching; using Aki.Reflection.Utils; using Aki.Common.Http; -using System.Linq; using System.Reflection; namespace Aki.Custom.Patches @@ -13,7 +12,7 @@ namespace Aki.Custom.Patches var methodName = "LoadCoreByString"; var flags = BindingFlags.Public | BindingFlags.Static; - return PatchConstants.EftTypes.Single(x => x.GetMethod(methodName, flags) != null) + return PatchConstants.EftTypes.SingleCustom(x => x.GetMethod(methodName, flags) != null) .GetMethod(methodName, flags); } diff --git a/project/Aki.Custom/Patches/CustomAiPatch.cs b/project/Aki.Custom/Patches/CustomAiPatch.cs index 8683ca3..7da2201 100644 --- a/project/Aki.Custom/Patches/CustomAiPatch.cs +++ b/project/Aki.Custom/Patches/CustomAiPatch.cs @@ -5,6 +5,7 @@ using Comfort.Common; using System.Reflection; using Aki.PrePatch; using Aki.Custom.CustomAI; +using HarmonyLib; namespace Aki.Custom.Patches { @@ -15,7 +16,7 @@ namespace Aki.Custom.Patches protected override MethodBase GetTargetMethod() { - return typeof(StandartBotBrain).GetMethod("Activate", BindingFlags.Public | BindingFlags.Instance); + return AccessTools.Method(typeof(StandartBotBrain), nameof(StandartBotBrain.Activate)); } /// diff --git a/project/Aki.Custom/Patches/EasyAssetsPatch.cs b/project/Aki.Custom/Patches/EasyAssetsPatch.cs index 5a2c01b..a2209b2 100644 --- a/project/Aki.Custom/Patches/EasyAssetsPatch.cs +++ b/project/Aki.Custom/Patches/EasyAssetsPatch.cs @@ -1,5 +1,4 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using Diz.Jobs; using Diz.Resources; using JetBrains.Annotations; @@ -15,24 +14,17 @@ using System.Threading.Tasks; using Aki.Custom.Models; using Aki.Custom.Utils; using DependencyGraph = DependencyGraph; +using Aki.Reflection.Utils; namespace Aki.Custom.Patches { public class EasyAssetsPatch : ModulePatch { - private static readonly FieldInfo _manifestField; private static readonly FieldInfo _bundlesField; - private static readonly PropertyInfo _systemProperty; static EasyAssetsPatch() { - var type = typeof(EasyAssets); - - _manifestField = type.GetField(nameof(EasyAssets.Manifest)); - _bundlesField = type.GetField($"{EasyBundleHelper.Type.Name.ToLowerInvariant()}_0", PatchConstants.PrivateFlags); - - // DependencyGraph - _systemProperty = type.GetProperty("System"); + _bundlesField = typeof(EasyAssets).GetField($"{EasyBundleHelper.Type.Name.ToLowerInvariant()}_0", PatchConstants.PrivateFlags); } public EasyAssetsPatch() @@ -45,7 +37,7 @@ namespace Aki.Custom.Patches protected override MethodBase GetTargetMethod() { - return typeof(EasyAssets).GetMethods(PatchConstants.PrivateFlags).Single(IsTargetMethod); + return typeof(EasyAssets).GetMethods(PatchConstants.PublicDeclaredFlags).SingleCustom(IsTargetMethod); } private static bool IsTargetMethod(MethodInfo mi) @@ -97,9 +89,9 @@ namespace Aki.Custom.Patches await JobScheduler.Yield(EJobPriority.Immediate); } - _manifestField.SetValue(instance, manifest); + instance.Manifest = manifest; _bundlesField.SetValue(instance, bundles); - _systemProperty.SetValue(instance, new DependencyGraph(bundles, defaultKey, shouldExclude)); + instance.System = new DependencyGraph(bundles, defaultKey, shouldExclude); } private static async Task GetManifestBundle(string filepath) diff --git a/project/Aki.Custom/Patches/EasyBundlePatch.cs b/project/Aki.Custom/Patches/EasyBundlePatch.cs index 998aff7..7dad516 100644 --- a/project/Aki.Custom/Patches/EasyBundlePatch.cs +++ b/project/Aki.Custom/Patches/EasyBundlePatch.cs @@ -1,4 +1,5 @@ -using Aki.Reflection.Patching; +using System; +using Aki.Reflection.Patching; using Diz.DependencyManager; using UnityEngine.Build.Pipeline; using System.IO; @@ -27,7 +28,7 @@ namespace Aki.Custom.Patches private static void PatchPostfix(object __instance, string key, string rootPath, CompatibilityAssetBundleManifest manifest, IBundleLock bundleLock) { var path = rootPath + key; - var dependencyKeys = manifest.GetDirectDependencies(key) ?? new string[0]; + var dependencyKeys = manifest.GetDirectDependencies(key) ?? Array.Empty(); if (BundleManager.Bundles.TryGetValue(key, out BundleInfo bundle)) { diff --git a/project/Aki.Custom/Patches/ExitWhileLootingPatch.cs b/project/Aki.Custom/Patches/ExitWhileLootingPatch.cs index 3354b62..87725ef 100644 --- a/project/Aki.Custom/Patches/ExitWhileLootingPatch.cs +++ b/project/Aki.Custom/Patches/ExitWhileLootingPatch.cs @@ -1,9 +1,8 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using Comfort.Common; using EFT; -using System.Linq; using System.Reflection; +using HarmonyLib; namespace Aki.Custom.Patches { @@ -14,8 +13,7 @@ namespace Aki.Custom.Patches { protected override MethodBase GetTargetMethod() { - return PatchConstants.EftTypes.Single(x => x.Name == "LocalGame").BaseType // BaseLocalGame - .GetMethod("Stop", BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance); + return AccessTools.Method(typeof(BaseLocalGame), nameof(BaseLocalGame.Stop)); } // Look at BaseLocalGame and find a method named "Stop" diff --git a/project/Aki.Custom/Patches/IsEnemyPatch.cs b/project/Aki.Custom/Patches/IsEnemyPatch.cs index 28fd774..8c331cc 100644 --- a/project/Aki.Custom/Patches/IsEnemyPatch.cs +++ b/project/Aki.Custom/Patches/IsEnemyPatch.cs @@ -1,35 +1,16 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT; -using System; using System.Linq; using System.Reflection; +using HarmonyLib; namespace Aki.Custom.Patches { public class IsEnemyPatch : ModulePatch { - private static Type _targetType; - private readonly string _targetMethodName = "IsEnemy"; - - public IsEnemyPatch() - { - _targetType = PatchConstants.EftTypes.Single(IsTargetType); - } - - private bool IsTargetType(Type type) - { - if (type.GetMethod("AddEnemy") != null && type.GetMethod("AddEnemyGroupIfAllowed") != null) - { - return true; - } - - return false; - } - protected override MethodBase GetTargetMethod() { - return _targetType.GetMethod(_targetMethodName); + return AccessTools.Method(typeof(BotsGroup), nameof(BotsGroup.IsEnemy)); } /// diff --git a/project/Aki.Custom/Patches/LocationLootCacheBustingPatch.cs b/project/Aki.Custom/Patches/LocationLootCacheBustingPatch.cs index 4c68fd0..ed4e38d 100644 --- a/project/Aki.Custom/Patches/LocationLootCacheBustingPatch.cs +++ b/project/Aki.Custom/Patches/LocationLootCacheBustingPatch.cs @@ -1,8 +1,7 @@ using Aki.Reflection.Patching; using Aki.Reflection.Utils; -using System; -using System.Linq; using System.Reflection; +using EFT; namespace Aki.Custom.Patches { @@ -13,8 +12,8 @@ namespace Aki.Custom.Patches { protected override MethodBase GetTargetMethod() { - var desiredType = PatchConstants.EftTypes.Single(x => x.Name == "LocalGame").BaseType; // BaseLocalGame - var desiredMethod = desiredType.GetMethods(PatchConstants.PrivateFlags).Single(x => IsTargetMethod(x)); // method_6 + var desiredType = typeof(BaseLocalGame); + var desiredMethod = desiredType.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public).SingleCustom(IsTargetMethod); // method_6 Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); diff --git a/project/Aki.Custom/Patches/OfflineRaidMenuPatch.cs b/project/Aki.Custom/Patches/OfflineRaidMenuPatch.cs index a746ebe..16b3879 100644 --- a/project/Aki.Custom/Patches/OfflineRaidMenuPatch.cs +++ b/project/Aki.Custom/Patches/OfflineRaidMenuPatch.cs @@ -7,6 +7,7 @@ using EFT.UI.Matchmaker; using System.Reflection; using EFT; using HarmonyLib; +using Aki.Reflection.Utils; namespace Aki.Custom.Patches { @@ -14,13 +15,8 @@ namespace Aki.Custom.Patches { protected override MethodBase GetTargetMethod() { - var desiredType = typeof(MatchmakerOfflineRaidScreen); - var desiredMethod = desiredType.GetMethod(nameof(MatchmakerOfflineRaidScreen.Show)); - - Logger.LogDebug($"{GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.GetDeclaredMethods(typeof(MatchmakerOfflineRaidScreen)) + .SingleCustom(m => m.Name == nameof(MatchmakerOfflineRaidScreen.Show) && m.GetParameters().Length == 1); } [PatchPrefix] diff --git a/project/Aki.Custom/Patches/OfflineRaidSettingsMenuPatch.cs b/project/Aki.Custom/Patches/OfflineRaidSettingsMenuPatch.cs index ff1f43f..e93cc0f 100644 --- a/project/Aki.Custom/Patches/OfflineRaidSettingsMenuPatch.cs +++ b/project/Aki.Custom/Patches/OfflineRaidSettingsMenuPatch.cs @@ -2,23 +2,15 @@ using Aki.Reflection.Patching; using EFT.UI; using EFT.UI.Matchmaker; +using HarmonyLib; namespace Aki.Custom.Patches { public class OfflineRaidSettingsMenuPatch : ModulePatch { - /// - /// RaidSettingsWindow.Show() - /// protected override MethodBase GetTargetMethod() { - var desiredType = typeof(RaidSettingsWindow); - var desiredMethod = desiredType.GetMethod(nameof(RaidSettingsWindow.Show)); - - Logger.LogDebug($"{GetType().Name} Type: {desiredType.Name}"); - Logger.LogDebug($"{GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(RaidSettingsWindow), nameof(RaidSettingsWindow.Show)); } [PatchPostfix] diff --git a/project/Aki.Custom/Patches/PmcFirstAidPatch.cs b/project/Aki.Custom/Patches/PmcFirstAidPatch.cs index 0dde0c8..8399e12 100644 --- a/project/Aki.Custom/Patches/PmcFirstAidPatch.cs +++ b/project/Aki.Custom/Patches/PmcFirstAidPatch.cs @@ -23,7 +23,7 @@ namespace Aki.Custom.Patches protected override MethodBase GetTargetMethod() { - return _targetType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic); + return _targetType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public); } /// diff --git a/project/Aki.Custom/Patches/QTEPatch.cs b/project/Aki.Custom/Patches/QTEPatch.cs index 3fff3c4..ed66a04 100644 --- a/project/Aki.Custom/Patches/QTEPatch.cs +++ b/project/Aki.Custom/Patches/QTEPatch.cs @@ -2,14 +2,16 @@ using Aki.Reflection.Patching; using System.Reflection; using EFT; -using Aki.Reflection.Utils; -using System.Linq; +using HarmonyLib; namespace Aki.Custom.Patches { public class QTEPatch : ModulePatch { - protected override MethodBase GetTargetMethod() => typeof(HideoutPlayerOwner).GetMethod(nameof(HideoutPlayerOwner.StopWorkout)); + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(HideoutPlayerOwner), nameof(HideoutPlayerOwner.StopWorkout)); + } [PatchPostfix] private static void PatchPostfix(HideoutPlayerOwner __instance) diff --git a/project/Aki.Custom/Patches/RagfairFeePatch.cs b/project/Aki.Custom/Patches/RagfairFeePatch.cs index ef3a781..ab90b72 100644 --- a/project/Aki.Custom/Patches/RagfairFeePatch.cs +++ b/project/Aki.Custom/Patches/RagfairFeePatch.cs @@ -1,9 +1,9 @@ using Aki.Common.Http; using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT.InventoryLogic; using EFT.UI.Ragfair; using System.Reflection; +using HarmonyLib; using UnityEngine; namespace Aki.Custom.Patches @@ -23,7 +23,7 @@ namespace Aki.Custom.Patches protected override MethodBase GetTargetMethod() { - return typeof(AddOfferWindow).GetMethod("method_1", PatchConstants.PrivateFlags); + return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.method_1)); } /// @@ -42,8 +42,7 @@ namespace Aki.Custom.Patches tpl = ___item_0.TemplateId, count = ___gclass3067_0.OfferItemCount, fee = Mathf.CeilToInt((float)GClass2084.CalculateTaxPrice(___item_0, ___gclass3067_0.OfferItemCount, ___double_0, ___bool_0)) - } - .ToJson()); + }.ToJson()); } } } \ No newline at end of file diff --git a/project/Aki.Custom/Patches/RaidSettingsWindowPatch.cs b/project/Aki.Custom/Patches/RaidSettingsWindowPatch.cs index 6e766aa..dcfd061 100644 --- a/project/Aki.Custom/Patches/RaidSettingsWindowPatch.cs +++ b/project/Aki.Custom/Patches/RaidSettingsWindowPatch.cs @@ -5,6 +5,7 @@ using Aki.Custom.Models; using EFT.UI; using EFT.UI.Matchmaker; using System.Reflection; +using HarmonyLib; namespace Aki.Custom.Patches { @@ -14,15 +15,12 @@ namespace Aki.Custom.Patches /// public class RaidSettingsWindowPatch : ModulePatch { + /// + /// Target method should have ~20 .UpdateValue() calls in it + /// protected override MethodBase GetTargetMethod() { - var desiredType = typeof(RaidSettingsWindow); - var desiredMethod = desiredType.GetMethod("method_8", BindingFlags.NonPublic | BindingFlags.Instance); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(RaidSettingsWindow), nameof(RaidSettingsWindow.method_8)); } [PatchPrefix] diff --git a/project/Aki.Custom/Patches/RankPanelPatch.cs b/project/Aki.Custom/Patches/RankPanelPatch.cs index 2a445e1..e62c9fb 100644 --- a/project/Aki.Custom/Patches/RankPanelPatch.cs +++ b/project/Aki.Custom/Patches/RankPanelPatch.cs @@ -1,9 +1,9 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using Comfort.Common; using EFT; using EFT.UI; using System.Reflection; +using HarmonyLib; namespace Aki.Custom.Patches { @@ -11,13 +11,7 @@ namespace Aki.Custom.Patches { protected override MethodBase GetTargetMethod() { - var desiredType = typeof(RankPanel); - var desiredMethod = desiredType.GetMethod("Show", PatchConstants.PublicFlags); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(RankPanel), nameof(RankPanel.Show)); } [PatchPrefix] diff --git a/project/Aki.Custom/Patches/ScavQuestPatch.cs b/project/Aki.Custom/Patches/ScavQuestPatch.cs index 3959cd6..7007bf6 100644 --- a/project/Aki.Custom/Patches/ScavQuestPatch.cs +++ b/project/Aki.Custom/Patches/ScavQuestPatch.cs @@ -1,10 +1,11 @@ -using Aki.Reflection.Patching; +using System.Linq; +using System.Reflection; +using Aki.Reflection.Patching; using Aki.Reflection.Utils; using EFT.UI.Matchmaker; -using System.Linq; -using System.Reflection; +using HarmonyLib; -namespace Aki.SinglePlayer.Patches.ScavMode +namespace Aki.Custom.Patches { /// /// Copy over scav-only quests from PMC profile to scav profile on pre-raid screen @@ -14,13 +15,8 @@ namespace Aki.SinglePlayer.Patches.ScavMode { protected override MethodBase GetTargetMethod() { - var desiredType = typeof(MatchmakerOfflineRaidScreen); - var desiredMethod = desiredType.GetMethod(nameof(MatchmakerOfflineRaidScreen.Show)); - - Logger.LogDebug($"{GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.GetDeclaredMethods(typeof(MatchmakerOfflineRaidScreen)) + .SingleCustom(m => m.Name == nameof(MatchmakerOfflineRaidScreen.Show) && m.GetParameters().Length == 1); } [PatchPostfix] diff --git a/project/Aki.Custom/Patches/SessionIdPatch.cs b/project/Aki.Custom/Patches/SessionIdPatch.cs index dbf33cc..ef2190e 100644 --- a/project/Aki.Custom/Patches/SessionIdPatch.cs +++ b/project/Aki.Custom/Patches/SessionIdPatch.cs @@ -1,8 +1,9 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT.UI; using System.IO; using System.Reflection; +using EFT; +using HarmonyLib; using UnityEngine; namespace Aki.Custom.Patches @@ -11,15 +12,10 @@ namespace Aki.Custom.Patches { private static PreloaderUI _preloader; - static SessionIdPatch() - { - _preloader = null; - } - protected override MethodBase GetTargetMethod() { - return PatchConstants.LocalGameType.BaseType.GetMethod("method_5", PatchConstants.PrivateFlags); - } + return AccessTools.Method(typeof(BaseLocalGame), nameof(BaseLocalGame.method_5)); + } [PatchPostfix] private static void PatchPostfix() diff --git a/project/Aki.Custom/Patches/SetLocationIdOnRaidStartPatch.cs b/project/Aki.Custom/Patches/SetLocationIdOnRaidStartPatch.cs index ec214f2..e1b5ae7 100644 --- a/project/Aki.Custom/Patches/SetLocationIdOnRaidStartPatch.cs +++ b/project/Aki.Custom/Patches/SetLocationIdOnRaidStartPatch.cs @@ -22,10 +22,11 @@ namespace Aki.Custom.Patches Type localGameBaseType = PatchConstants.LocalGameType.BaseType; // At this point, gameWorld.MainPlayer isn't set, so we need to use the LocalGame's `Location_0` property - _locationProperty = localGameBaseType.GetProperties(PatchConstants.PrivateFlags).Single(x => x.PropertyType == typeof(Location)); + _locationProperty = localGameBaseType.GetProperties(PatchConstants.PublicDeclaredFlags) + .SingleCustom(x => x.PropertyType == typeof(Location)); // Find the TimeAndWeatherSettings handling method - var desiredMethod = localGameBaseType.GetMethods(PatchConstants.PrivateFlags).SingleOrDefault(m => IsTargetMethod(m)); + var desiredMethod = localGameBaseType.GetMethods(PatchConstants.PublicDeclaredFlags).SingleOrDefault(IsTargetMethod); Logger.LogDebug($"{GetType().Name} Type: {localGameBaseType?.Name}"); Logger.LogDebug($"{GetType().Name} Method: {desiredMethod?.Name}"); @@ -52,6 +53,13 @@ namespace Aki.Custom.Patches } Location location = _locationProperty.GetValue(__instance) as Location; + + if (location == null) + { + Logger.LogError($"[SetLocationId] Failed to get location data"); + return; + } + gameWorld.LocationId = location.Id; Logger.LogDebug($"[SetLocationId] Set locationId to: {location.Id}"); diff --git a/project/Aki.Custom/Patches/VersionLabelPatch.cs b/project/Aki.Custom/Patches/VersionLabelPatch.cs index 6a665ac..7ba51e5 100644 --- a/project/Aki.Custom/Patches/VersionLabelPatch.cs +++ b/project/Aki.Custom/Patches/VersionLabelPatch.cs @@ -5,7 +5,6 @@ using Aki.Reflection.Utils; using Aki.Custom.Models; using EFT.UI; using HarmonyLib; -using System.Linq; using System.Reflection; using Comfort.Common; @@ -17,18 +16,9 @@ namespace Aki.Custom.Patches protected override MethodBase GetTargetMethod() { - try - { - return PatchConstants.EftTypes - .Single(x => x.GetField("Taxonomy", BindingFlags.Public | BindingFlags.Instance) != null) + return PatchConstants.EftTypes + .SingleCustom(x => x.GetField("Taxonomy", BindingFlags.Public | BindingFlags.Instance) != null) .GetMethod("Create", BindingFlags.Public | BindingFlags.Static); - } - catch (System.Exception e) - { - Logger.LogInfo($"VersionLabelPatch failed {e.Message} {e.StackTrace} {e.InnerException.StackTrace}"); - throw; - } - } [PatchPostfix] @@ -45,7 +35,7 @@ namespace Aki.Custom.Patches Traverse.Create(Singleton.Instance).Field("string_2").SetValue(_versionLabel); var major = Traverse.Create(__result).Field("Major"); var existingValue = major.GetValue(); - major.SetValue($"{existingValue} {_versionLabel}" ); + major.SetValue($"{existingValue} {_versionLabel}"); } } } \ No newline at end of file diff --git a/project/Aki.Custom/Utils/EasyBundleHelper.cs b/project/Aki.Custom/Utils/EasyBundleHelper.cs index f3fdf40..84f09e6 100644 --- a/project/Aki.Custom/Utils/EasyBundleHelper.cs +++ b/project/Aki.Custom/Utils/EasyBundleHelper.cs @@ -11,7 +11,7 @@ namespace Aki.Custom.Utils { public class EasyBundleHelper { - private const BindingFlags _NonPublicInstanceflags = BindingFlags.Instance | BindingFlags.NonPublic; + private const BindingFlags NonPublicInstanceFlags = BindingFlags.Instance | BindingFlags.NonPublic; private static readonly FieldInfo _pathField; private static readonly FieldInfo _keyWithoutExtensionField; private static readonly FieldInfo _bundleLockField; @@ -27,24 +27,28 @@ namespace Aki.Custom.Utils _ = nameof(IBundleLock.IsLocked); _ = nameof(BindableState.Bind); - // Class can be found as a private array inside EasyAssets.cs, next to DependencyGraph - Type = PatchConstants.EftTypes.Single(x => x.GetMethod("set_SameNameAsset", _NonPublicInstanceflags) != null); + Type = PatchConstants.EftTypes.SingleCustom(x => !x.IsInterface && x.GetProperty("SameNameAsset", PatchConstants.PublicDeclaredFlags) != null); - _pathField = Type.GetField("string_1", _NonPublicInstanceflags); - _keyWithoutExtensionField = Type.GetField("string_0", _NonPublicInstanceflags); - _bundleLockField = Type.GetFields(_NonPublicInstanceflags).FirstOrDefault(x => x.FieldType == typeof(IBundleLock)); + _pathField = Type.GetField("string_1", NonPublicInstanceFlags); + _keyWithoutExtensionField = Type.GetField("string_0", NonPublicInstanceFlags); + _bundleLockField = Type.GetFields(NonPublicInstanceFlags).FirstOrDefault(x => x.FieldType == typeof(IBundleLock)); _dependencyKeysProperty = Type.GetProperty("DependencyKeys"); _keyProperty = Type.GetProperty("Key"); _loadStateProperty = Type.GetProperty("LoadState"); // Function with 0 params and returns task (usually method_0()) - var possibleMethods = Type.GetMethods(_NonPublicInstanceflags).Where(x => x.GetParameters().Length == 0 && x.ReturnType == typeof(Task)); - if (possibleMethods.Count() > 1) + var possibleMethods = Type.GetMethods(PatchConstants.PublicDeclaredFlags).Where(x => x.GetParameters().Length == 0 && x.ReturnType == typeof(Task)).ToArray(); + if (possibleMethods.Length > 1) { - Console.WriteLine($"Unable to find desired method as there are multiple possible matches: {string.Join(",", possibleMethods.Select(x => x.Name))}"); + throw new Exception($"Unable to find the Loading Coroutine method as there are multiple possible matches: {string.Join(",", possibleMethods.Select(x => x.Name))}"); } - _loadingCoroutineMethod = possibleMethods.SingleOrDefault(); + if (possibleMethods.Length == 0) + { + throw new Exception("Unable to find the Loading Coroutine method as there are no matches"); + } + + _loadingCoroutineMethod = possibleMethods.Single(); } public EasyBundleHelper(object easyBundle) diff --git a/project/Aki.Debugging/Patches/CoordinatesPatch.cs b/project/Aki.Debugging/Patches/CoordinatesPatch.cs index 0be13a0..70927d5 100644 --- a/project/Aki.Debugging/Patches/CoordinatesPatch.cs +++ b/project/Aki.Debugging/Patches/CoordinatesPatch.cs @@ -1,51 +1,47 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT; using TMPro; using UnityEngine; using System; using System.Reflection; +using HarmonyLib; namespace Aki.Debugging.Patches { public class CoordinatesPatch : ModulePatch { private static TextMeshProUGUI _alphaLabel; - private static PropertyInfo _playerProperty; protected override MethodBase GetTargetMethod() { - var localGameBaseType = PatchConstants.LocalGameType.BaseType; - _playerProperty = localGameBaseType.GetProperty("PlayerOwner", BindingFlags.Public | BindingFlags.Instance); - return localGameBaseType.GetMethod("Update", PatchConstants.PrivateFlags); + return AccessTools.Method(typeof(BaseLocalGame), nameof(BaseLocalGame.Update)); } [PatchPrefix] - private static void PatchPrefix(object __instance) + private static void PatchPrefix(BaseLocalGame __instance) { - if (Input.GetKeyDown(KeyCode.LeftControl)) + if (!Input.GetKeyDown(KeyCode.LeftControl)) return; + + if (_alphaLabel == null) { - if (_alphaLabel == null) - { - _alphaLabel = GameObject.Find("AlphaLabel").GetComponent(); - _alphaLabel.color = Color.green; - _alphaLabel.fontSize = 22; - _alphaLabel.font = Resources.Load("Fonts & Materials/ARIAL SDF"); - } - - var playerOwner = (GamePlayerOwner)_playerProperty.GetValue(__instance); - var aiming = LookingRaycast(playerOwner.Player); - - if (_alphaLabel != null) - { - _alphaLabel.text = $"Looking at: [{aiming.x}, {aiming.y}, {aiming.z}]"; - Logger.LogInfo(_alphaLabel.text); - } - - var position = playerOwner.transform.position; - var rotation = playerOwner.transform.rotation.eulerAngles; - Logger.LogInfo($"Character position: [{position.x},{position.y},{position.z}] | Rotation: [{rotation.x},{rotation.y},{rotation.z}]"); + _alphaLabel = GameObject.Find("AlphaLabel").GetComponent(); + _alphaLabel.color = Color.green; + _alphaLabel.fontSize = 22; + _alphaLabel.font = Resources.Load("Fonts & Materials/ARIAL SDF"); } + + var playerOwner = __instance.PlayerOwner; + var aiming = LookingRaycast(playerOwner.Player); + + if (_alphaLabel != null) + { + _alphaLabel.text = $"Looking at: [{aiming.x}, {aiming.y}, {aiming.z}]"; + Logger.LogInfo(_alphaLabel.text); + } + + var position = playerOwner.transform.position; + var rotation = playerOwner.transform.rotation.eulerAngles; + Logger.LogInfo($"Character position: [{position.x},{position.y},{position.z}] | Rotation: [{rotation.x},{rotation.y},{rotation.z}]"); } public static Vector3 LookingRaycast(Player player) diff --git a/project/Aki.Debugging/Patches/DataHandlerDebugPatch.cs b/project/Aki.Debugging/Patches/DataHandlerDebugPatch.cs new file mode 100644 index 0000000..30e7cd7 --- /dev/null +++ b/project/Aki.Debugging/Patches/DataHandlerDebugPatch.cs @@ -0,0 +1,21 @@ +using System; +using System.Reflection; +using Aki.Reflection.Patching; +using HarmonyLib; + +namespace Aki.Debugging.Patches +{ + public class DataHandlerDebugPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(DataHandler), nameof(DataHandler.method_5)); + } + + [PatchPostfix] + private static void PatchPrefix(ref string __result) + { + Console.WriteLine($"response json: ${__result}"); + } + } +} \ No newline at end of file diff --git a/project/Aki.Debugging/Patches/EndRaidDebug.cs b/project/Aki.Debugging/Patches/EndRaidDebug.cs index 2afc2d1..3adc994 100644 --- a/project/Aki.Debugging/Patches/EndRaidDebug.cs +++ b/project/Aki.Debugging/Patches/EndRaidDebug.cs @@ -1,9 +1,9 @@ using Aki.Reflection.Patching; using System.Reflection; -using Aki.Reflection.Utils; using BepInEx.Logging; using EFT; using EFT.UI; +using HarmonyLib; using TMPro; namespace Aki.Debugging.Patches @@ -12,7 +12,7 @@ namespace Aki.Debugging.Patches { protected override MethodBase GetTargetMethod() { - return typeof(TraderCard).GetMethod("method_0", PatchConstants.PrivateFlags); + return AccessTools.Method(typeof(TraderCard), nameof(TraderCard.method_0)); } [PatchPrefix] diff --git a/project/Aki.Reflection/Utils/PatchConstants.cs b/project/Aki.Reflection/Utils/PatchConstants.cs index 1f25785..3c8f7ce 100644 --- a/project/Aki.Reflection/Utils/PatchConstants.cs +++ b/project/Aki.Reflection/Utils/PatchConstants.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using Comfort.Common; @@ -11,6 +12,7 @@ namespace Aki.Reflection.Utils { public static BindingFlags PrivateFlags { get; private set; } public static BindingFlags PublicFlags { get; private set; } + public static BindingFlags PublicDeclaredFlags { get; private set; } public static Type[] EftTypes { get; private set; } public static Type[] FilesCheckerTypes { get; private set; } public static Type LocalGameType { get; private set; } @@ -36,14 +38,48 @@ namespace Aki.Reflection.Utils { _ = nameof(ISession.GetPhpSessionId); - PrivateFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; + PrivateFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly; PublicFlags = BindingFlags.Public | BindingFlags.Instance; + PublicDeclaredFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; EftTypes = typeof(AbstractGame).Assembly.GetTypes(); FilesCheckerTypes = typeof(ICheckResult).Assembly.GetTypes(); - LocalGameType = EftTypes.Single(x => x.Name == "LocalGame"); - ExfilPointManagerType = EftTypes.Single(x => x.GetMethod("InitAllExfiltrationPoints") != null); - SessionInterfaceType = EftTypes.Single(x => x.GetMethods().Select(y => y.Name).Contains("GetPhpSessionId") && x.IsInterface); - BackendSessionInterfaceType = EftTypes.Single(x => x.GetMethods().Select(y => y.Name).Contains("ChangeProfileStatus") && x.IsInterface); + LocalGameType = EftTypes.SingleCustom(x => x.Name == "LocalGame"); + ExfilPointManagerType = EftTypes.SingleCustom(x => x.GetMethod("InitAllExfiltrationPoints") != null); + SessionInterfaceType = EftTypes.SingleCustom(x => x.GetMethods().Select(y => y.Name).Contains("GetPhpSessionId") && x.IsInterface); + BackendSessionInterfaceType = EftTypes.SingleCustom(x => x.GetMethods().Select(y => y.Name).Contains("ChangeProfileStatus") && x.IsInterface); + } + + /// + /// A custom LINQ .Single() implementation with improved logging for easier patch debugging + /// + /// A single member of the input sequence that matches the given search pattern + /// + /// + public static T SingleCustom(this IEnumerable types, Func predicate) where T : MemberInfo + { + if (types == null) + { + throw new ArgumentNullException(nameof(types)); + } + + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + var matchingTypes = types.Where(predicate).ToArray(); + + if (matchingTypes.Length > 1) + { + throw new InvalidOperationException($"More than one member matches the specified search pattern: {string.Join(", ", matchingTypes.Select(t => t.Name))}"); + } + + if (matchingTypes.Length == 0) + { + throw new InvalidOperationException("No members match the specified search pattern"); + } + + return matchingTypes[0]; } } } diff --git a/project/Aki.SinglePlayer/Patches/Healing/HealthControllerPatch.cs b/project/Aki.SinglePlayer/Patches/Healing/HealthControllerPatch.cs index 77aec85..d73942d 100644 --- a/project/Aki.SinglePlayer/Patches/Healing/HealthControllerPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Healing/HealthControllerPatch.cs @@ -4,24 +4,25 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.Healing { /// - /// HealthController used by post-raid heal screen and health listenen class are different, this patch + /// HealthController used by post-raid heal screen and health listener class are different, this patch /// ensures effects (fracture/bleeding) on body parts stay in sync /// public class HealthControllerPatch : ModulePatch { protected override MethodBase GetTargetMethod() { - return typeof(HealthControllerClass).GetMethod("ApplyTreatment", BindingFlags.Public | BindingFlags.Instance); + return AccessTools.Method(typeof(HealthControllerClass), nameof(HealthControllerClass.ApplyTreatment)); } [PatchPrefix] private static void PatchPrefix(object healthObserver) { - var property = healthObserver.GetType().GetProperty("Effects"); + var property = AccessTools.Property(healthObserver.GetType(), "Effects"); if (property != null) { var effects = property.GetValue(healthObserver); diff --git a/project/Aki.SinglePlayer/Patches/Healing/MainMenuControllerPatch.cs b/project/Aki.SinglePlayer/Patches/Healing/MainMenuControllerPatch.cs index 1e801d3..d7b99a0 100644 --- a/project/Aki.SinglePlayer/Patches/Healing/MainMenuControllerPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Healing/MainMenuControllerPatch.cs @@ -1,7 +1,7 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT.HealthSystem; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.Healing { @@ -15,13 +15,7 @@ namespace Aki.SinglePlayer.Patches.Healing protected override MethodBase GetTargetMethod() { - var desiredType = typeof(MainMenuController); - var desiredMethod = desiredType.GetMethod("method_1", PatchConstants.PrivateFlags); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(MainMenuController), nameof(MainMenuController.method_1)); } [PatchPostfix] diff --git a/project/Aki.SinglePlayer/Patches/Healing/PlayerPatch.cs b/project/Aki.SinglePlayer/Patches/Healing/PlayerPatch.cs index b974584..ca6da82 100644 --- a/project/Aki.SinglePlayer/Patches/Healing/PlayerPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Healing/PlayerPatch.cs @@ -1,9 +1,9 @@ using System; using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT; using System.Reflection; using System.Threading.Tasks; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.Healing { @@ -11,13 +11,7 @@ namespace Aki.SinglePlayer.Patches.Healing { protected override MethodBase GetTargetMethod() { - var desiredType = typeof(Player); - var desiredMethod = desiredType.GetMethod("Init", PatchConstants.PrivateFlags); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(Player), nameof(Player.Init)); } [PatchPostfix] diff --git a/project/Aki.SinglePlayer/Patches/Healing/PostRaidHealScreenPatch.cs b/project/Aki.SinglePlayer/Patches/Healing/PostRaidHealScreenPatch.cs index c43b6a7..996f06c 100644 --- a/project/Aki.SinglePlayer/Patches/Healing/PostRaidHealScreenPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Healing/PostRaidHealScreenPatch.cs @@ -17,10 +17,8 @@ namespace Aki.SinglePlayer.Patches.Healing { // Class1049.smethod_0 as of 18969 //internal static Class1049 smethod_0(GInterface29 backend, string profileId, Profile savageProfile, LocationSettingsClass.GClass1097 location, ExitStatus exitStatus, TimeSpan exitTime, ERaidMode raidMode) - var desiredType = PatchConstants.EftTypes.Single(x => x.Name == "PostRaidHealthScreenClass"); - var desiredMethod = desiredType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Single(IsTargetMethod); + var desiredMethod = typeof(PostRaidHealthScreenClass).GetMethods(BindingFlags.Static | BindingFlags.Public).SingleCustom(IsTargetMethod); - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); return desiredMethod; @@ -40,10 +38,9 @@ namespace Aki.SinglePlayer.Patches.Healing } [PatchPrefix] - private static bool PatchPrefix(TarkovApplication __instance, ref ERaidMode raidMode) + private static bool PatchPrefix(ref ERaidMode raidMode) { raidMode = ERaidMode.Online; - return true; // Perform original method } } diff --git a/project/Aki.SinglePlayer/Patches/MainMenu/InsuranceScreenPatch.cs b/project/Aki.SinglePlayer/Patches/MainMenu/InsuranceScreenPatch.cs index c69e0d6..8e72086 100644 --- a/project/Aki.SinglePlayer/Patches/MainMenu/InsuranceScreenPatch.cs +++ b/project/Aki.SinglePlayer/Patches/MainMenu/InsuranceScreenPatch.cs @@ -1,6 +1,7 @@ using Aki.Reflection.Patching; using EFT; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.MainMenu { @@ -17,7 +18,7 @@ namespace Aki.SinglePlayer.Patches.MainMenu protected override MethodBase GetTargetMethod() { //[CompilerGenerated] - //private void method_67() + //private void method_XX() //{ // if (this.raidSettings_0.SelectedLocation.Id == "laboratory") // { @@ -31,13 +32,7 @@ namespace Aki.SinglePlayer.Patches.MainMenu // this.method_41(); //} - var desiredType = typeof(MainMenuController); - var desiredMethod = desiredType.GetMethod("method_71", BindingFlags.NonPublic | BindingFlags.Instance); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(MainMenuController), nameof(MainMenuController.method_71)); } [PatchPrefix] diff --git a/project/Aki.SinglePlayer/Patches/MainMenu/MapReadyButtonPatch.cs b/project/Aki.SinglePlayer/Patches/MainMenu/MapReadyButtonPatch.cs index de2094f..102631c 100644 --- a/project/Aki.SinglePlayer/Patches/MainMenu/MapReadyButtonPatch.cs +++ b/project/Aki.SinglePlayer/Patches/MainMenu/MapReadyButtonPatch.cs @@ -1,20 +1,21 @@ -using System.Reflection; +using System.Linq; +using System.Reflection; using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT.UI; using EFT.UI.Matchmaker; namespace Aki.SinglePlayer.Patches.MainMenu { /// - /// Removes the 'ready' button from the map preview screen - accessible by choosing map to deply to > clicking 'map' bottom left of screen - /// Clicking the ready button makes a call to client/match/available, something we dont want + /// Removes the 'ready' button from the map preview screen - accessible by choosing map to deploy to > clicking 'map' bottom left of screen + /// Clicking the ready button makes a call to client/match/available, something we don't want that /// public class MapReadyButtonPatch : ModulePatch { protected override MethodBase GetTargetMethod() { - return typeof(MatchmakerMapPointsScreen).GetMethod("Show", PatchConstants.PrivateFlags); + // We don't really care which "Show" method is returned - either will do + return typeof(MatchmakerMapPointsScreen).GetMethods().First(m => m.Name == nameof(MatchmakerMapPointsScreen.Show)); } [PatchPostfix] diff --git a/project/Aki.SinglePlayer/Patches/MainMenu/PluginErrorNotifierPatch.cs b/project/Aki.SinglePlayer/Patches/MainMenu/PluginErrorNotifierPatch.cs index 20c81a5..2b38a5a 100644 --- a/project/Aki.SinglePlayer/Patches/MainMenu/PluginErrorNotifierPatch.cs +++ b/project/Aki.SinglePlayer/Patches/MainMenu/PluginErrorNotifierPatch.cs @@ -1,10 +1,8 @@ using Aki.Common.Utils; using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using BepInEx.Bootstrap; using EFT.Communications; using EFT.UI; -using HarmonyLib; using System.Linq; using System.Reflection; using System.Text; @@ -19,16 +17,12 @@ namespace Aki.SinglePlayer.Patches.MainMenu **/ internal class PluginErrorNotifierPatch : ModulePatch { - private static MethodInfo _displayMessageNotificationMethod; private static bool _messageShown = false; protected override MethodBase GetTargetMethod() { - _displayMessageNotificationMethod = AccessTools.Method(typeof(NotificationManagerClass), "DisplayMessageNotification"); - - var desiredType = typeof(MenuScreen); - var desiredMethod = desiredType.GetMethod("Show", PatchConstants.PrivateFlags); - return desiredMethod; + // We don't really care which "Show" method is returned - either will do + return typeof(MenuScreen).GetMethods().First(m => m.Name == nameof(MenuScreen.Show)); } [PatchPostfix] @@ -45,7 +39,7 @@ namespace Aki.SinglePlayer.Patches.MainMenu // Show a toast in the bottom right of the screen indicating how many plugins failed to load var consoleHeaderMessage = $"{failedPluginCount} plugin{(failedPluginCount > 1 ? "s" : "")} failed to load due to errors"; var toastMessage = $"{consoleHeaderMessage}. Please check the console for details."; - _displayMessageNotificationMethod.Invoke(null, new object[] { toastMessage, ENotificationDurationType.Infinite, ENotificationIconType.Alert, Color.red }); + NotificationManagerClass.DisplayMessageNotification(toastMessage, ENotificationDurationType.Infinite, ENotificationIconType.Alert, Color.red); // Build the error message we'll put in the BepInEx and in-game consoles var stringBuilder = new StringBuilder(); diff --git a/project/Aki.SinglePlayer/Patches/MainMenu/SelectLocationScreenPatch.cs b/project/Aki.SinglePlayer/Patches/MainMenu/SelectLocationScreenPatch.cs index 6415054..a1fa83b 100644 --- a/project/Aki.SinglePlayer/Patches/MainMenu/SelectLocationScreenPatch.cs +++ b/project/Aki.SinglePlayer/Patches/MainMenu/SelectLocationScreenPatch.cs @@ -1,8 +1,8 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT.UI; using EFT.UI.Matchmaker; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.MainMenu { @@ -13,13 +13,7 @@ namespace Aki.SinglePlayer.Patches.MainMenu { protected override MethodBase GetTargetMethod() { - var desiredType = typeof(MatchMakerSelectionLocationScreen); - var desiredMethod = desiredType.GetMethod("Awake", PatchConstants.PrivateFlags); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(MatchMakerSelectionLocationScreen), nameof(MatchMakerSelectionLocationScreen.Awake)); } [PatchPostfix] diff --git a/project/Aki.SinglePlayer/Patches/Progression/ExperienceGainPatch.cs b/project/Aki.SinglePlayer/Patches/Progression/ExperienceGainPatch.cs index c53ee9f..95deae2 100644 --- a/project/Aki.SinglePlayer/Patches/Progression/ExperienceGainPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Progression/ExperienceGainPatch.cs @@ -1,8 +1,8 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT.UI.SessionEnd; -using System.Linq; using System.Reflection; +using EFT; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.Progression { @@ -10,25 +10,9 @@ namespace Aki.SinglePlayer.Patches.Progression { protected override MethodBase GetTargetMethod() { - var desiredType = typeof(SessionResultExperienceCount); - var desiredMethod = desiredType.GetMethods(PatchConstants.PrivateFlags).FirstOrDefault(IsTargetMethod); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(SessionResultExperienceCount), nameof(SessionResultExperienceCount.Show), new []{ typeof(Profile), typeof(bool), typeof(ExitStatus) }); } - - private static bool IsTargetMethod(MethodInfo mi) - { - var parameters = mi.GetParameters(); - return (parameters.Length == 3 - && parameters[0].Name == "profile" - && parameters[1].Name == "isOnline" - && parameters[2].Name == "exitStatus" - && parameters[1].ParameterType == typeof(bool)); - } - + [PatchPrefix] private static void PatchPrefix(ref bool isOnline) { diff --git a/project/Aki.SinglePlayer/Patches/Progression/LighthouseBridgePatch.cs b/project/Aki.SinglePlayer/Patches/Progression/LighthouseBridgePatch.cs index 157a145..1bf0475 100644 --- a/project/Aki.SinglePlayer/Patches/Progression/LighthouseBridgePatch.cs +++ b/project/Aki.SinglePlayer/Patches/Progression/LighthouseBridgePatch.cs @@ -3,6 +3,7 @@ using Aki.SinglePlayer.Models.Progression; using Comfort.Common; using EFT; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.Progression { @@ -10,7 +11,7 @@ namespace Aki.SinglePlayer.Patches.Progression { protected override MethodBase GetTargetMethod() { - return typeof(GameWorld).GetMethod(nameof(GameWorld.OnGameStarted)); + return AccessTools.Method(typeof(GameWorld), nameof(GameWorld.OnGameStarted)); } [PatchPostfix] diff --git a/project/Aki.SinglePlayer/Patches/Progression/LighthouseTransmitterPatch.cs b/project/Aki.SinglePlayer/Patches/Progression/LighthouseTransmitterPatch.cs index 4160d5f..a7caeb6 100644 --- a/project/Aki.SinglePlayer/Patches/Progression/LighthouseTransmitterPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Progression/LighthouseTransmitterPatch.cs @@ -2,6 +2,7 @@ using Comfort.Common; using EFT; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.Progression { @@ -9,7 +10,7 @@ namespace Aki.SinglePlayer.Patches.Progression { protected override MethodBase GetTargetMethod() { - return typeof(RadioTransmitterHandlerClass).GetMethod("method_4", BindingFlags.NonPublic | BindingFlags.Instance); + return AccessTools.Method(typeof(RadioTransmitterHandlerClass), nameof(RadioTransmitterHandlerClass.method_4)); } [PatchPrefix] diff --git a/project/Aki.SinglePlayer/Patches/Progression/MidRaidQuestChangePatch.cs b/project/Aki.SinglePlayer/Patches/Progression/MidRaidQuestChangePatch.cs index cd0cad9..f4d85ae 100644 --- a/project/Aki.SinglePlayer/Patches/Progression/MidRaidQuestChangePatch.cs +++ b/project/Aki.SinglePlayer/Patches/Progression/MidRaidQuestChangePatch.cs @@ -8,13 +8,13 @@ using System.Reflection; namespace Aki.SinglePlayer.Patches.Progression { /// - /// After picking up a quest item, trigger CheckForStatusChange() from the questController to fully update a quest subtasks to show (e.g. `survive and extract item from raid` task) + /// After picking up a quest item, trigger CheckForStatusChange() from the questController to fully update a quest sub-tasks to show (e.g. `survive and extract item from raid` task) /// public class MidRaidQuestChangePatch : ModulePatch { protected override MethodBase GetTargetMethod() { - return typeof(Profile).GetMethod("AddToCarriedQuestItems", BindingFlags.Public | BindingFlags.Instance); + return AccessTools.Method(typeof(Profile), nameof(Profile.AddToCarriedQuestItems)); } [PatchPostfix] @@ -23,7 +23,7 @@ namespace Aki.SinglePlayer.Patches.Progression var gameWorld = Singleton.Instance; if (gameWorld == null) { - Logger.LogDebug($"[MidRaidQuestChangePatch] gameWorld instance was null"); + Logger.LogDebug("[MidRaidQuestChangePatch] gameWorld instance was null"); return; } diff --git a/project/Aki.SinglePlayer/Patches/Progression/OfflineSaveProfilePatch.cs b/project/Aki.SinglePlayer/Patches/Progression/OfflineSaveProfilePatch.cs index ea93885..0409f2a 100644 --- a/project/Aki.SinglePlayer/Patches/Progression/OfflineSaveProfilePatch.cs +++ b/project/Aki.SinglePlayer/Patches/Progression/OfflineSaveProfilePatch.cs @@ -14,7 +14,7 @@ using System.Reflection; namespace Aki.SinglePlayer.Patches.Progression { /// - /// After a raid, the client doesnt update the server on what occurred during the raid. To persist loot/quest changes etc we + /// After a raid, the client doesn't update the server on what occurred during the raid. To persist loot/quest changes etc we /// make the client send the active profile to a spt-specific endpoint `/raid/profile/save` where we can update the players profile json /// public class OfflineSaveProfilePatch : ModulePatch @@ -35,8 +35,8 @@ namespace Aki.SinglePlayer.Patches.Progression { // method_45 - as of 16432 // method_43 - as of 18876 - var desiredType = PatchConstants.EftTypes.Single(x => x.Name == "TarkovApplication"); - var desiredMethod = Array.Find(desiredType.GetMethods(PatchConstants.PrivateFlags), IsTargetMethod); + var desiredType = typeof(TarkovApplication); + var desiredMethod = Array.Find(desiredType.GetMethods(PatchConstants.PublicDeclaredFlags), IsTargetMethod); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); @@ -66,7 +66,7 @@ namespace Aki.SinglePlayer.Patches.Progression { Exit = result.Value0.ToString().ToLowerInvariant(), // Exit player used to leave raid Profile = profile, // players scav or pmc profile, depending on type of raid they did - Health = Utils.Healing.HealthListener.Instance.CurrentHealth, // Speicifc limb/effect damage data the player has at end of raid + Health = Utils.Healing.HealthListener.Instance.CurrentHealth, // Specific limb/effect damage data the player has at end of raid Insurance = Utils.Insurance.InsuredItemManager.Instance.GetTrackedItems(), // A copy of items insured by player with durability values as of raid end (if item is returned, send it back with correct durability values) IsPlayerScav = ____raidSettings.IsScav }; diff --git a/project/Aki.SinglePlayer/Patches/Progression/OfflineSpawnPointPatch.cs b/project/Aki.SinglePlayer/Patches/Progression/OfflineSpawnPointPatch.cs index f242a67..6c9c7d3 100644 --- a/project/Aki.SinglePlayer/Patches/Progression/OfflineSpawnPointPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Progression/OfflineSpawnPointPatch.cs @@ -18,9 +18,9 @@ namespace Aki.SinglePlayer.Patches.Progression protected override MethodBase GetTargetMethod() { - var desiredType = PatchConstants.EftTypes.First(IsTargetType); + var desiredType = typeof(SpawnSystemClass); var desiredMethod = desiredType - .GetMethods(PatchConstants.PrivateFlags) + .GetMethods(PatchConstants.PublicDeclaredFlags) .First(m => m.Name.Contains("SelectSpawnPoint")); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); @@ -34,7 +34,7 @@ namespace Aki.SinglePlayer.Patches.Progression // GClass1812 as of 17349 // GClass1886 as of 18876 // Remapped to SpawnSystemClass - return (type.GetMethods(PatchConstants.PrivateFlags).Any(x => x.Name.IndexOf("CheckFarthestFromOtherPlayers", StringComparison.OrdinalIgnoreCase) != -1) + return (type.GetMethods(PatchConstants.PublicDeclaredFlags).Any(x => x.Name.IndexOf("CheckFarthestFromOtherPlayers", StringComparison.OrdinalIgnoreCase) != -1) && type.IsClass); } @@ -48,7 +48,7 @@ namespace Aki.SinglePlayer.Patches.Progression IPlayer person, string infiltration) { - var spawnPointsField = (ISpawnPoints)__instance.GetType().GetFields(PatchConstants.PrivateFlags).SingleOrDefault(f => f.FieldType == typeof(ISpawnPoints))?.GetValue(__instance); + var spawnPointsField = (ISpawnPoints)__instance.GetType().GetFields(PatchConstants.PublicDeclaredFlags).SingleOrDefault(f => f.FieldType == typeof(ISpawnPoints))?.GetValue(__instance); if (spawnPointsField == null) { throw new Exception($"OfflineSpawnPointPatch: Failed to locate field: {nameof(ISpawnPoints)} on class instance: {__instance.GetType().Name}"); diff --git a/project/Aki.SinglePlayer/Patches/Progression/ScavExperienceGainPatch.cs b/project/Aki.SinglePlayer/Patches/Progression/ScavExperienceGainPatch.cs index 8c1f2ff..5d57b48 100644 --- a/project/Aki.SinglePlayer/Patches/Progression/ScavExperienceGainPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Progression/ScavExperienceGainPatch.cs @@ -1,10 +1,10 @@ +using System; using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT; using EFT.Counters; using EFT.UI.SessionEnd; -using System.Linq; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.Progression { @@ -19,15 +19,13 @@ namespace Aki.SinglePlayer.Patches.Progression /// protected override MethodBase GetTargetMethod() { - var desiredType = typeof(SessionResultExitStatus); - var desiredMethod = desiredType.GetMethods(PatchConstants.PrivateFlags).FirstOrDefault(IsTargetMethod); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method( + typeof(SessionResultExitStatus), + nameof(SessionResultExitStatus.Show), + new []{ typeof(Profile), typeof(PlayerVisualRepresentation), typeof(ESideType), typeof(ExitStatus), typeof(TimeSpan), typeof(ISession), typeof(bool) }); } + // Unused, but left here in case patch breaks and finding the intended method is difficult private static bool IsTargetMethod(MethodInfo mi) { var parameters = mi.GetParameters(); diff --git a/project/Aki.SinglePlayer/Patches/Quests/DogtagPatch.cs b/project/Aki.SinglePlayer/Patches/Quests/DogtagPatch.cs index 4b8cf49..db19a70 100644 --- a/project/Aki.SinglePlayer/Patches/Quests/DogtagPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Quests/DogtagPatch.cs @@ -3,26 +3,21 @@ using EFT; using EFT.InventoryLogic; using System; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.Quests { public class DogtagPatch : ModulePatch { - private static BindingFlags _flags; - private static PropertyInfo _getEquipmentProperty; - static DogtagPatch() { _ = nameof(EquipmentClass.GetSlot); _ = nameof(DamageInfo.Weapon); - - _flags = BindingFlags.Instance | BindingFlags.NonPublic; - _getEquipmentProperty = typeof(Player).GetProperty("Equipment", _flags); } protected override MethodBase GetTargetMethod() { - return typeof(Player).GetMethod("OnBeenKilledByAggressor", _flags); + return AccessTools.Method(typeof(Player), nameof(Player.OnBeenKilledByAggressor)); } /// @@ -65,7 +60,7 @@ namespace Aki.SinglePlayer.Patches.Quests private static Item GetDogTagItemFromPlayerWhoDied(Player __instance) { - var equipment = (EquipmentClass)_getEquipmentProperty.GetValue(__instance); + var equipment = __instance.Equipment; if (equipment == null) { Logger.LogError("DogtagPatch error > Player has no equipment"); diff --git a/project/Aki.SinglePlayer/Patches/Quests/EndByTimerPatch.cs b/project/Aki.SinglePlayer/Patches/Quests/EndByTimerPatch.cs index 10586bc..1c1184d 100644 --- a/project/Aki.SinglePlayer/Patches/Quests/EndByTimerPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Quests/EndByTimerPatch.cs @@ -1,9 +1,8 @@ using Aki.Common.Http; using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT; -using System.Linq; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.Quests { @@ -15,15 +14,10 @@ namespace Aki.SinglePlayer.Patches.Quests { protected override MethodBase GetTargetMethod() { - var desiredType = PatchConstants.LocalGameType.BaseType; - var desiredMethod = desiredType.GetMethods(PatchConstants.PrivateFlags).SingleOrDefault(IsStopRaidMethod); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(BaseLocalGame), nameof(BaseLocalGame.Stop)); } + // Unused, but left here in case patch breaks and finding the intended method is difficult private static bool IsStopRaidMethod(MethodInfo mi) { var parameters = mi.GetParameters(); @@ -39,13 +33,13 @@ namespace Aki.SinglePlayer.Patches.Quests } [PatchPrefix] - private static bool PrefixPatch(object __instance, ref ExitStatus exitStatus, ref string exitName) + private static bool PrefixPatch(ref ExitStatus exitStatus, ref string exitName) { var isParsed = bool.TryParse(RequestHandler.GetJson("/singleplayer/settings/raid/endstate"), out bool MIAOnRaidEnd); if (isParsed) { // No extract name and successful, its a MIA - if (MIAOnRaidEnd == true && string.IsNullOrEmpty(exitName?.Trim()) && exitStatus == ExitStatus.Survived) + if (MIAOnRaidEnd && string.IsNullOrEmpty(exitName?.Trim()) && exitStatus == ExitStatus.Survived) { exitStatus = ExitStatus.MissingInAction; exitName = null; diff --git a/project/Aki.SinglePlayer/Patches/Quests/SpawnPmcPatch.cs b/project/Aki.SinglePlayer/Patches/Quests/SpawnPmcPatch.cs index f425067..459e227 100644 --- a/project/Aki.SinglePlayer/Patches/Quests/SpawnPmcPatch.cs +++ b/project/Aki.SinglePlayer/Patches/Quests/SpawnPmcPatch.cs @@ -11,8 +11,8 @@ namespace Aki.SinglePlayer.Patches.Quests { protected override MethodBase GetTargetMethod() { - var desiredType = PatchConstants.EftTypes.Single(IsTargetType); - var desiredMethod = desiredType.GetMethod("method_1", PatchConstants.PrivateFlags); + var desiredType = PatchConstants.EftTypes.SingleCustom(IsTargetType); + var desiredMethod = desiredType.GetMethod("method_1", PatchConstants.PublicDeclaredFlags); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); @@ -22,7 +22,7 @@ namespace Aki.SinglePlayer.Patches.Quests private static bool IsTargetType(Type type) { - if (!typeof(IGetProfileData).IsAssignableFrom(type) || type.GetMethod("method_1", PatchConstants.PrivateFlags) == null) + if (!typeof(IGetProfileData).IsAssignableFrom(type) || type.GetMethod("method_1", PatchConstants.PublicDeclaredFlags) == null) { return false; } @@ -32,7 +32,7 @@ namespace Aki.SinglePlayer.Patches.Quests } [PatchPrefix] - private static bool PatchPrefix(ref bool __result, object __instance, WildSpawnType ___wildSpawnType_0, BotDifficulty ___botDifficulty_0, Profile x) + private static bool PatchPrefix(ref bool __result, WildSpawnType ___wildSpawnType_0, BotDifficulty ___botDifficulty_0, Profile x) { if (x == null) { diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/BotTemplateLimitPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/BotTemplateLimitPatch.cs index 4768120..b09a0f4 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/BotTemplateLimitPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/BotTemplateLimitPatch.cs @@ -1,9 +1,9 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using Aki.Common.Http; using System; using System.Collections.Generic; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.RaidFix { @@ -17,13 +17,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix protected override MethodBase GetTargetMethod() { - var desiredType = typeof(BotsPresets); - var desiredMethod = desiredType.GetMethod("method_1", PatchConstants.PrivateFlags); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(BotsPresets), nameof(BotsPresets.method_1)); } [PatchPostfix] diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/EmptyInfilFixPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/EmptyInfilFixPatch.cs index 3a1de3f..7b1db4b 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/EmptyInfilFixPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/EmptyInfilFixPatch.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Reflection; using Aki.Reflection.Patching; @@ -7,9 +6,7 @@ using Aki.Reflection.Utils; using Comfort.Common; using EFT; using EFT.Game.Spawning; -using EFT.UI; using UnityEngine; -using Object = UnityEngine.Object; namespace Aki.SinglePlayer.Patches.RaidFix { @@ -23,8 +20,8 @@ namespace Aki.SinglePlayer.Patches.RaidFix { var desiredType = PatchConstants.LocalGameType.BaseType; var desiredMethod = desiredType - .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.CreateInstance) - .Single(IsTargetMethod); + .GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.CreateInstance) + .SingleCustom(IsTargetMethod); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/GetNewBotTemplatesPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/GetNewBotTemplatesPatch.cs index 453bce1..293f69e 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/GetNewBotTemplatesPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/GetNewBotTemplatesPatch.cs @@ -7,13 +7,12 @@ using Aki.Reflection.Utils; using Aki.SinglePlayer.Models.RaidFix; using System; using System.Collections.Generic; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.RaidFix { public class GetNewBotTemplatesPatch : ModulePatch { - private static MethodInfo _getNewProfileMethod; - static GetNewBotTemplatesPatch() { _ = nameof(IGetProfileData.PrepareToLoadBackend); @@ -22,29 +21,16 @@ namespace Aki.SinglePlayer.Patches.RaidFix _ = nameof(JobPriority.General); } - /// - /// BotsPresets.GetNewProfile() - /// - public GetNewBotTemplatesPatch() - { - var desiredType = typeof(BotsPresets); - _getNewProfileMethod = desiredType - .GetMethod(nameof(BotsPresets.GetNewProfile), BindingFlags.Instance | BindingFlags.NonPublic); // want the func with 2 params (protected + inherited from base) - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {_getNewProfileMethod?.Name}"); - } - /// /// Looking for CreateProfile() /// /// protected override MethodBase GetTargetMethod() { - return typeof(BotsPresets).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) - .Single(x => IsTargetMethod(x)); + return AccessTools.DeclaredMethod(typeof(BotsPresets), nameof(BotsPresets.CreateProfile)); } + // Unused, but left here in case patch breaks and finding the intended method is difficult private bool IsTargetMethod(MethodInfo mi) { var parameters = mi.GetParameters(); @@ -72,7 +58,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix try { // Force true to ensure bot profile is deleted after use - _getNewProfileMethod.Invoke(__instance, new object[] { data, true }); + __instance.GetNewProfile(data, true); } catch (Exception e) { diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/InsuredItemManagerStartPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/InsuredItemManagerStartPatch.cs index 2e1c8c0..823295c 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/InsuredItemManagerStartPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/InsuredItemManagerStartPatch.cs @@ -3,6 +3,7 @@ using EFT; using System.Reflection; using Aki.SinglePlayer.Utils.Insurance; using Comfort.Common; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.RaidFix { @@ -10,7 +11,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix { protected override MethodBase GetTargetMethod() { - return typeof(GameWorld).GetMethod(nameof(GameWorld.OnGameStarted)); + return AccessTools.Method(typeof(GameWorld), nameof(GameWorld.OnGameStarted)); } [PatchPostfix] diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/LabsKeycardRemovalPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/LabsKeycardRemovalPatch.cs index 1b81924..656581d 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/LabsKeycardRemovalPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/LabsKeycardRemovalPatch.cs @@ -16,7 +16,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix protected override MethodBase GetTargetMethod() { - return typeof(GameWorld).GetMethod(nameof(GameWorld.OnGameStarted)); + return AccessTools.Method(typeof(GameWorld), nameof(GameWorld.OnGameStarted)); } [PatchPostfix] diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/MaxBotPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/MaxBotPatch.cs index 18b728b..b5c6ef4 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/MaxBotPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/MaxBotPatch.cs @@ -1,7 +1,6 @@ using Aki.Common.Http; using Aki.Reflection.Patching; using Aki.Reflection.Utils; -using System.Linq; using System.Reflection; namespace Aki.SinglePlayer.Patches.RaidFix @@ -15,7 +14,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix { const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; const string methodName = "SetSettings"; - var desiredType = PatchConstants.EftTypes.Single(x => x.GetMethod(methodName, flags) != null && IsTargetMethod(x.GetMethod(methodName, flags))); + var desiredType = PatchConstants.EftTypes.SingleCustom(x => x.GetMethod(methodName, flags) != null && IsTargetMethod(x.GetMethod(methodName, flags))); var desiredMethod = desiredType.GetMethod(methodName, flags); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/PlayerToggleSoundFixPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/PlayerToggleSoundFixPatch.cs index 26e8b09..d2f0749 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/PlayerToggleSoundFixPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/PlayerToggleSoundFixPatch.cs @@ -2,6 +2,7 @@ using System.Reflection; using Aki.Reflection.Patching; using Comfort.Common; using EFT; +using HarmonyLib; using UnityEngine; namespace Aki.SinglePlayer.Patches.RaidFix @@ -13,7 +14,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix { protected override MethodBase GetTargetMethod() { - return typeof(Player).GetMethod("PlayToggleSound", BindingFlags.Instance | BindingFlags.NonPublic); + return AccessTools.Method(typeof(Player), nameof(Player.PlayToggleSound)); } [PatchPrefix] diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/PostRaidHealingPricePatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/PostRaidHealingPricePatch.cs index af35958..b900f6a 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/PostRaidHealingPricePatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/PostRaidHealingPricePatch.cs @@ -10,13 +10,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix { protected override MethodBase GetTargetMethod() { - var desiredType = typeof(Profile.TraderInfo); - var desiredMethod = desiredType.GetMethod("UpdateLevel", BindingFlags.NonPublic | BindingFlags.Instance); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(Profile.TraderInfo), nameof(Profile.TraderInfo.UpdateLevel)); } [PatchPrefix] @@ -36,7 +30,8 @@ namespace Aki.SinglePlayer.Patches.RaidFix } // Backing field of the "CurrentLoyalty" property - Traverse.Create(__instance).Field("k__BackingField").SetValue(loyaltyLevelSettings.Value); + // Traverse.Create(__instance).Field("k__BackingField").SetValue(loyaltyLevelSettings.Value); + __instance.CurrentLoyalty = loyaltyLevelSettings.Value; } } } \ No newline at end of file diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/RemoveUsedBotProfilePatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/RemoveUsedBotProfilePatch.cs index 10a8b79..1e4cb90 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/RemoveUsedBotProfilePatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/RemoveUsedBotProfilePatch.cs @@ -1,48 +1,25 @@ using Aki.Reflection.Patching; -using Aki.Reflection.Utils; -using EFT; -using System; -using System.Linq; using System.Reflection; +using Aki.Reflection.Utils; namespace Aki.SinglePlayer.Patches.RaidFix { public class RemoveUsedBotProfilePatch : ModulePatch { - private static readonly BindingFlags _flags; - private static readonly Type _targetInterface; - private static readonly Type _targetType; - private static readonly FieldInfo _profilesField; - static RemoveUsedBotProfilePatch() { _ = nameof(IGetProfileData.ChooseProfile); - - _flags = BindingFlags.Instance | BindingFlags.NonPublic; - _targetInterface = PatchConstants.EftTypes.Single(IsTargetInterface); - _targetType = typeof(BotsPresets); - _profilesField = _targetType.GetField("list_0", _flags); } protected override MethodBase GetTargetMethod() { - return _targetType.GetMethod("GetNewProfile", _flags); + return typeof(BotsPresets).BaseType.GetMethods().SingleCustom(m => m.Name == nameof(BotsPresets.GetNewProfile) && m.IsVirtual); } - - private static bool IsTargetInterface(Type type) - { - return type.IsInterface && type.GetProperty("StartProfilesLoaded") != null && type.GetMethod("CreateProfile") != null; - } - - private static bool IsTargetType(Type type) - { - return _targetInterface.IsAssignableFrom(type) && _targetInterface.IsAssignableFrom(type.BaseType); - } - + /// /// BotsPresets.GetNewProfile() [PatchPrefix] - private static bool PatchPrefix(ref Profile __result, object __instance, GClass560 data, ref bool withDelete) + private static bool PatchPrefix(ref bool withDelete) { withDelete = true; diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/SmokeGrenadeFuseSoundFixPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/SmokeGrenadeFuseSoundFixPatch.cs index ad95d89..edc5b01 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/SmokeGrenadeFuseSoundFixPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/SmokeGrenadeFuseSoundFixPatch.cs @@ -16,7 +16,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix { protected override MethodBase GetTargetMethod() { - return typeof(GrenadeEmission).GetMethod(nameof(GrenadeEmission.StartEmission)); + return AccessTools.Method(typeof(GrenadeEmission), nameof(GrenadeEmission.StartEmission)); } [PatchTranspiler] diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/SpawnProcessNegativeValuePatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/SpawnProcessNegativeValuePatch.cs index 33d5b32..9cf0cb6 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/SpawnProcessNegativeValuePatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/SpawnProcessNegativeValuePatch.cs @@ -3,6 +3,7 @@ using Aki.Reflection.Utils; using EFT; using System; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.RaidFix { @@ -19,19 +20,12 @@ namespace Aki.SinglePlayer.Patches.RaidFix { protected override MethodBase GetTargetMethod() { - var desiredType = typeof(BotSpawner); - var desiredMethod = desiredType.GetMethod("CheckOnMax", PatchConstants.PublicFlags); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(BotSpawner), nameof(BotSpawner.CheckOnMax)); } [PatchPrefix] private static bool PatchPreFix(int wantSpawn, ref int toDelay, ref int toSpawn, ref int ____maxBots, int ____allBotsCount, int ____inSpawnProcess) { - // Set bots to delay if alive bots + spawning bots count > maxbots // ____inSpawnProcess can be negative, don't go below 0 when calculating if ((____allBotsCount + Math.Max(____inSpawnProcess, 0)) > ____maxBots) diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/TinnitusFixPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/TinnitusFixPatch.cs index 240dd2b..d2f54ff 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/TinnitusFixPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/TinnitusFixPatch.cs @@ -4,6 +4,7 @@ using System.Reflection; using Aki.Reflection.Patching; using System.Collections; using EFT.HealthSystem; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.RaidFix { @@ -11,15 +12,16 @@ namespace Aki.SinglePlayer.Patches.RaidFix { protected override MethodBase GetTargetMethod() { - return typeof(BetterAudio).GetMethod("StartTinnitusEffect", BindingFlags.Instance | BindingFlags.Public); + return AccessTools.Method(typeof(BetterAudio), nameof(BetterAudio.StartTinnitusEffect)); } // checks on invoke whether the player is stunned before allowing tinnitus [PatchPrefix] static bool PatchPrefix() { - bool shouldInvoke = typeof(ActiveHealthController) - .GetMethod("FindActiveEffect", BindingFlags.Instance | BindingFlags.Public) + var baseMethod = AccessTools.Method(typeof(ActiveHealthController), nameof(ActiveHealthController.FindActiveEffect)); + + bool shouldInvoke = baseMethod .MakeGenericMethod(typeof(ActiveHealthController) .GetNestedType("Stun", BindingFlags.Instance | BindingFlags.NonPublic)) .Invoke(Singleton.Instance.MainPlayer.ActiveHealthController, new object[] { EBodyPart.Common }) != null; diff --git a/project/Aki.SinglePlayer/Patches/RaidFix/VoIPTogglerPatch.cs b/project/Aki.SinglePlayer/Patches/RaidFix/VoIPTogglerPatch.cs index 0fbf443..aa16439 100644 --- a/project/Aki.SinglePlayer/Patches/RaidFix/VoIPTogglerPatch.cs +++ b/project/Aki.SinglePlayer/Patches/RaidFix/VoIPTogglerPatch.cs @@ -1,7 +1,7 @@ using System.Reflection; using Aki.Reflection.Patching; -using Aki.Reflection.Utils; using EFT; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.RaidFix { @@ -9,7 +9,7 @@ namespace Aki.SinglePlayer.Patches.RaidFix { protected override MethodBase GetTargetMethod() { - return typeof(ForceMuteVoIPToggler).GetMethod("Awake", PatchConstants.PrivateFlags); + return AccessTools.Method(typeof(ForceMuteVoIPToggler), nameof(ForceMuteVoIPToggler.Awake)); } [PatchPrefix] diff --git a/project/Aki.SinglePlayer/Patches/ScavMode/ExfilPointManagerPatch.cs b/project/Aki.SinglePlayer/Patches/ScavMode/ExfilPointManagerPatch.cs index 4dc694c..c9b6d9e 100644 --- a/project/Aki.SinglePlayer/Patches/ScavMode/ExfilPointManagerPatch.cs +++ b/project/Aki.SinglePlayer/Patches/ScavMode/ExfilPointManagerPatch.cs @@ -2,6 +2,7 @@ using Aki.Reflection.Patching; using Comfort.Common; using EFT; using System.Reflection; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.ScavMode { @@ -12,13 +13,7 @@ namespace Aki.SinglePlayer.Patches.ScavMode { protected override MethodBase GetTargetMethod() { - var desiredType = typeof(GameWorld); - var desiredMethod = desiredType.GetMethod("OnGameStarted", BindingFlags.Public | BindingFlags.Instance); - - Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); - Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); - - return desiredMethod; + return AccessTools.Method(typeof(GameWorld), nameof(GameWorld.OnGameStarted)); } [PatchPostfix] @@ -26,21 +21,19 @@ namespace Aki.SinglePlayer.Patches.ScavMode { var gameWorld = Singleton.Instance; - // checks nothing is null otherwise woopsies happen. + // checks nothing is null otherwise bad things happen if (gameWorld == null || gameWorld.RegisteredPlayers == null || gameWorld.ExfiltrationController == null) { - Logger.LogError("Unable to Find Gameworld or RegisterPlayers... Unable to Disable Extracts for Scav raid"); + Logger.LogError("Could not find GameWorld or RegisterPlayers... Unable to disable extracts for Scav raid"); } Player player = gameWorld.MainPlayer; - var exfilController = gameWorld.ExfiltrationController; - - // Only disable PMC extracts if current player is a scav. + // Only disable PMC extracts if current player is a scav if (player.Fraction == ETagStatus.Scav && player.Location != "hideout") { - // these are PMC extracts only, scav extracts are under a different field called ScavExfiltrationPoints. - foreach (var exfil in exfilController.ExfiltrationPoints) + // these are PMC extracts only, scav extracts are under a different field called ScavExfiltrationPoints + foreach (var exfil in gameWorld.ExfiltrationController.ExfiltrationPoints) { exfil.Disable(); } diff --git a/project/Aki.SinglePlayer/Patches/ScavMode/LoadOfflineRaidScreenPatch.cs b/project/Aki.SinglePlayer/Patches/ScavMode/LoadOfflineRaidScreenPatch.cs index 82ce2e3..50be17e 100644 --- a/project/Aki.SinglePlayer/Patches/ScavMode/LoadOfflineRaidScreenPatch.cs +++ b/project/Aki.SinglePlayer/Patches/ScavMode/LoadOfflineRaidScreenPatch.cs @@ -31,12 +31,10 @@ namespace Aki.SinglePlayer.Patches.ScavMode _ = nameof(WavesSettings.IsBosses); _ = GClass3164.MAX_SCAV_COUNT; // UPDATE REFS TO THIS CLASS BELOW !!! - var menuControllerType = typeof(MainMenuController); - // `MatchmakerInsuranceScreen` OnShowNextScreen - _onReadyScreenMethod = menuControllerType.GetMethod("method_42", PatchConstants.PrivateFlags); + _onReadyScreenMethod = AccessTools.Method(typeof(MainMenuController), nameof(MainMenuController.method_42)); - _isLocalField = menuControllerType.GetField("bool_0", PatchConstants.PrivateFlags); + _isLocalField = AccessTools.Field(typeof(MainMenuController), "bool_0"); _menuControllerField = typeof(TarkovApplication).GetFields(PatchConstants.PrivateFlags).FirstOrDefault(x => x.FieldType == typeof(MainMenuController)); if (_menuControllerField == null) @@ -48,7 +46,7 @@ namespace Aki.SinglePlayer.Patches.ScavMode protected override MethodBase GetTargetMethod() { // `MatchMakerSelectionLocationScreen` OnShowNextScreen - return typeof(MainMenuController).GetMethod("method_68", PatchConstants.PrivateFlags); + return AccessTools.Method(typeof(MainMenuController), nameof(MainMenuController.method_68)); } [PatchTranspiler] @@ -125,7 +123,7 @@ namespace Aki.SinglePlayer.Patches.ScavMode gclass.OnShowNextScreen += LoadOfflineRaidNextScreen; // `MatchmakerOfflineRaidScreen` OnShowReadyScreen - gclass.OnShowReadyScreen += (OfflineRaidAction)Delegate.CreateDelegate(typeof(OfflineRaidAction), menuController, "method_72"); + gclass.OnShowReadyScreen += (OfflineRaidAction)Delegate.CreateDelegate(typeof(OfflineRaidAction), menuController, nameof(MainMenuController.method_72)); gclass.ShowScreen(EScreenState.Queued); } diff --git a/project/Aki.SinglePlayer/Patches/ScavMode/ScavExfilPatch.cs b/project/Aki.SinglePlayer/Patches/ScavMode/ScavExfilPatch.cs index ec2f498..58a64d6 100644 --- a/project/Aki.SinglePlayer/Patches/ScavMode/ScavExfilPatch.cs +++ b/project/Aki.SinglePlayer/Patches/ScavMode/ScavExfilPatch.cs @@ -3,6 +3,7 @@ using Aki.Reflection.Patching; using Comfort.Common; using EFT; using EFT.Interactive; +using HarmonyLib; namespace Aki.SinglePlayer.Patches.ScavMode { @@ -10,7 +11,7 @@ namespace Aki.SinglePlayer.Patches.ScavMode { protected override MethodBase GetTargetMethod() { - return typeof(ExfiltrationControllerClass).GetMethod("EligiblePoints", new []{ typeof(Profile) }); + return AccessTools.Method(typeof(ExfiltrationControllerClass), nameof(ExfiltrationControllerClass.EligiblePoints), new[] { typeof(Profile) }); } [PatchPrefix] @@ -29,7 +30,7 @@ namespace Aki.SinglePlayer.Patches.ScavMode } // Running this prepares all the data for getting scav exfil points - __instance.ScavExfiltrationClaim(Singleton.Instance.MainPlayer.Position, profile.Id, profile.FenceInfo.AvailableExitsCount); + __instance.ScavExfiltrationClaim(((IPlayer)Singleton.Instance.MainPlayer).Position, profile.Id, profile.FenceInfo.AvailableExitsCount); // Get the required mask value and retrieve a list of exfil points, setting it as the result var mask = __instance.GetScavExfiltrationMask(profile.Id); diff --git a/project/Aki.SinglePlayer/Patches/ScavMode/ScavLateStartPatch.cs b/project/Aki.SinglePlayer/Patches/ScavMode/ScavLateStartPatch.cs index 9092270..bfefce1 100644 --- a/project/Aki.SinglePlayer/Patches/ScavMode/ScavLateStartPatch.cs +++ b/project/Aki.SinglePlayer/Patches/ScavMode/ScavLateStartPatch.cs @@ -25,8 +25,8 @@ namespace Aki.SinglePlayer.Patches.ScavMode protected override MethodBase GetTargetMethod() { - var desiredType = PatchConstants.EftTypes.Single(x => x.Name == "TarkovApplication"); - var desiredMethod = Array.Find(desiredType.GetMethods(PatchConstants.PrivateFlags), IsTargetMethod); + var desiredType = typeof(TarkovApplication); + var desiredMethod = Array.Find(desiredType.GetMethods(PatchConstants.PublicDeclaredFlags), IsTargetMethod); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}"); @@ -95,11 +95,10 @@ namespace Aki.SinglePlayer.Patches.ScavMode foreach (var exitChange in exitChangesToApply) { // Find the client exit we want to make changes to - var exitToChange = location.exits.First(x => x.Name == exitChange.Name); + var exitToChange = location.exits.FirstOrDefault(x => x.Name == exitChange.Name); if (exitToChange == null) { Logger.LogDebug($"Exit with Id: {exitChange.Name} not found, skipping"); - continue; } @@ -111,6 +110,10 @@ namespace Aki.SinglePlayer.Patches.ScavMode if (exitChange.MinTime.HasValue) { exitToChange.MinTime = exitChange.MinTime.Value; + } + + if (exitChange.MaxTime.HasValue) + { exitToChange.MaxTime = exitChange.MaxTime.Value; } } @@ -131,19 +134,9 @@ namespace Aki.SinglePlayer.Patches.ScavMode } // Reset values to those from cache - if (clientLocationExit.Chance != cachedExit.Chance) - { - clientLocationExit.Chance = cachedExit.Chance; - } - if (clientLocationExit.MinTime != cachedExit.MinTime) - { - clientLocationExit.MinTime = cachedExit.MinTime; - } - - if (clientLocationExit.MaxTime != cachedExit.MaxTime) - { - clientLocationExit.MaxTime = cachedExit.MaxTime; - } + clientLocationExit.Chance = cachedExit.Chance; + clientLocationExit.MinTime = cachedExit.MinTime; + clientLocationExit.MaxTime = cachedExit.MaxTime; } } diff --git a/project/Aki.SinglePlayer/Patches/ScavMode/ScavPrefabLoadPatch.cs b/project/Aki.SinglePlayer/Patches/ScavMode/ScavPrefabLoadPatch.cs index 564fd90..026eca5 100644 --- a/project/Aki.SinglePlayer/Patches/ScavMode/ScavPrefabLoadPatch.cs +++ b/project/Aki.SinglePlayer/Patches/ScavMode/ScavPrefabLoadPatch.cs @@ -15,13 +15,13 @@ namespace Aki.SinglePlayer.Patches.ScavMode protected override MethodBase GetTargetMethod() { var desiredType = typeof(TarkovApplication) - .GetNestedTypes(PatchConstants.PrivateFlags) - .Single(x => x.GetField("timeAndWeather") != null - && x.GetField("tarkovApplication_0") != null - && x.GetField("timeHasComeScreenController") == null - && x.Name.Contains("Struct")); + .GetNestedTypes(PatchConstants.PublicDeclaredFlags) + .SingleCustom(x => x.GetField("timeAndWeather") != null + && x.GetField("tarkovApplication_0") != null + && x.GetField("timeHasComeScreenController") == null + && x.Name.Contains("Struct")); - var desiredMethod = desiredType.GetMethods(PatchConstants.PrivateFlags) + var desiredMethod = desiredType.GetMethods(PatchConstants.PublicDeclaredFlags) .FirstOrDefault(x => x.Name == "MoveNext"); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); diff --git a/project/Aki.SinglePlayer/Patches/ScavMode/ScavProfileLoadPatch.cs b/project/Aki.SinglePlayer/Patches/ScavMode/ScavProfileLoadPatch.cs index 2395d84..b457a90 100644 --- a/project/Aki.SinglePlayer/Patches/ScavMode/ScavProfileLoadPatch.cs +++ b/project/Aki.SinglePlayer/Patches/ScavMode/ScavProfileLoadPatch.cs @@ -17,12 +17,12 @@ namespace Aki.SinglePlayer.Patches.ScavMode { // Struct225 - 20575 var desiredType = typeof(TarkovApplication) - .GetNestedTypes(PatchConstants.PrivateFlags) - .Single(x => x.GetField("timeAndWeather") != null + .GetNestedTypes(PatchConstants.PublicDeclaredFlags) + .SingleCustom(x => x.GetField("timeAndWeather") != null && x.GetField("timeHasComeScreenController") != null && x.Name.Contains("Struct")); - var desiredMethod = desiredType.GetMethods(PatchConstants.PrivateFlags) + var desiredMethod = desiredType.GetMethods(PatchConstants.PublicDeclaredFlags) .FirstOrDefault(x => x.Name == "MoveNext"); Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}"); @@ -72,7 +72,7 @@ namespace Aki.SinglePlayer.Patches.ScavMode new Code(OpCodes.Callvirt, PatchConstants.BackendSessionInterfaceType, "get_Profile"), new Code(OpCodes.Br, brLabel), new CodeWithLabel(OpCodes.Callvirt, brFalseLabel, PatchConstants.SessionInterfaceType, "get_ProfileOfPet"), - new CodeWithLabel(OpCodes.Stfld, brLabel, typeof(TarkovApplication).GetNestedTypes(BindingFlags.NonPublic).Single(IsTargetNestedType), "profile") + new CodeWithLabel(OpCodes.Stfld, brLabel, typeof(TarkovApplication).GetNestedTypes(BindingFlags.Public).SingleCustom(IsTargetNestedType), "profile") }); codes.RemoveRange(searchIndex, 4); @@ -83,7 +83,7 @@ namespace Aki.SinglePlayer.Patches.ScavMode private static bool IsTargetNestedType(System.Type nestedType) { - return nestedType.GetMethods(PatchConstants.PrivateFlags) + return nestedType.GetMethods(PatchConstants.PublicDeclaredFlags) .Count(x => x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(IResult)) > 0 && nestedType.GetField("savageProfile") != null; } } diff --git a/project/Aki.SinglePlayer/Utils/Insurance/InsuredItemManager.cs b/project/Aki.SinglePlayer/Utils/Insurance/InsuredItemManager.cs index ad1980d..b173a64 100644 --- a/project/Aki.SinglePlayer/Utils/Insurance/InsuredItemManager.cs +++ b/project/Aki.SinglePlayer/Utils/Insurance/InsuredItemManager.cs @@ -33,7 +33,7 @@ namespace Aki.SinglePlayer.Utils.Insurance public List GetTrackedItems() { var itemsToSend = new List(); - if (_items == null || _items.Count() == 0) + if (_items == null || !_items.Any()) { return itemsToSend; } diff --git a/project/Shared/Hollowed/hollowed.dll b/project/Shared/Hollowed/hollowed.dll index de08dff..5560408 100644 Binary files a/project/Shared/Hollowed/hollowed.dll and b/project/Shared/Hollowed/hollowed.dll differ