diff --git a/Nautilus/Patchers/SMLHelperCompatibilityPatcher.cs b/Nautilus/Patchers/SMLHelperCompatibilityPatcher.cs index b16d5e6d..873da48f 100644 --- a/Nautilus/Patchers/SMLHelperCompatibilityPatcher.cs +++ b/Nautilus/Patchers/SMLHelperCompatibilityPatcher.cs @@ -13,19 +13,45 @@ namespace Nautilus.Patchers; // This class can be SAFELY removed if we ever decide to make Nautilus incompatible with SMLHelper (which it already kinda is...) internal class SMLHelperCompatibilityPatcher { - private const string SMLHarmonyInstance = "com.ahk1221.smlhelper"; // This string is both the harmony instance & plugin GUID. + public const string SMLHarmonyInstance = "com.ahk1221.smlhelper"; // This string is both the harmony instance & plugin GUID. + public const string QModManagerGUID = "QModManager.QMMLoader"; private const string SMLAssemblyName = "SMLHelper"; + private const string SMLHelperModJsonID = "SMLHelper"; private static Assembly _smlHelperAssembly; - internal static void Patch(Harmony harmony) + private static bool? _smlHelperInstalled; + + public static bool SMLHelperInstalled { - if (BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey(SMLHarmonyInstance)) + get { - CoroutineHost.StartCoroutine(WaitOnSMLHelperForPatches(harmony)); + if (!_smlHelperInstalled.HasValue) + { + _smlHelperInstalled = GetSMLHelperExists(); + } + return _smlHelperInstalled.Value; } } + private static bool GetSMLHelperExists() + { +#if SUBNAUTICA + return BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey(SMLHarmonyInstance); +#elif BELOWZERO + if (!BepInEx.Bootstrap.Chainloader.PluginInfos.TryGetValue(QModManagerGUID, out var qmodManager)) + return false; + var qmodServices = Assembly.GetAssembly(qmodManager.Instance.GetType()).GetType("QModManager.API.QModServices"); + var qmodServicesInstance = AccessTools.PropertyGetter(qmodServices, "Main").Invoke(null, new object[0]); + return (bool)AccessTools.Method(qmodServices, "ModPresent").Invoke(qmodServicesInstance, new object[] { SMLHelperModJsonID }); +#endif + } + + internal static void Patch(Harmony harmony) + { + CoroutineHost.StartCoroutine(WaitOnSMLHelperForPatches(harmony)); + } + private static IEnumerator WaitOnSMLHelperForPatches(Harmony harmony) { // This code is arbitrary but was taken from an older version of SMLHelper so that this patch applies only AFTER has been patched. @@ -44,10 +70,18 @@ private static IEnumerator WaitOnSMLHelperForPatches(Harmony harmony) yield return null; + if (!SMLHelperInstalled) + { + yield break; + } + InternalLogger.Log("Patching SMLHelper compatibility fixes", BepInEx.Logging.LogLevel.Info); + // Finally apply the patches: + UnpatchSMLOptionsMethods(harmony); FixSMLOptionsException(harmony); + UnpatchSMLTooltipPatches(harmony); } private static void UnpatchSMLOptionsMethods(Harmony harmony) @@ -114,6 +148,14 @@ private static bool ChangeAdjusterComponentPrefix(object __instance, ref Type __ return false; } + // We don't want duplicate tooltips!! + private static void UnpatchSMLTooltipPatches(Harmony harmony) + { + harmony.Unpatch(AccessTools.Method(typeof(TooltipFactory), nameof(TooltipFactory.BuildTech)), HarmonyPatchType.Postfix, SMLHarmonyInstance); + harmony.Unpatch(AccessTools.Method(typeof(TooltipFactory), nameof(TooltipFactory.ItemCommons)), HarmonyPatchType.Postfix, SMLHarmonyInstance); + harmony.Unpatch(AccessTools.Method(typeof(TooltipFactory), nameof(TooltipFactory.CraftRecipe)), HarmonyPatchType.Postfix, SMLHarmonyInstance); + } + private static Assembly GetSMLAssembly() { if (_smlHelperAssembly != null) @@ -130,7 +172,7 @@ private static Assembly GetSMLAssembly() return _smlHelperAssembly; } - private static Type GetSMLType(string typeName) + internal static Type GetSMLType(string typeName) { var assembly = GetSMLAssembly(); return assembly.GetType(typeName); diff --git a/Nautilus/Patchers/TooltipPatcher.cs b/Nautilus/Patchers/TooltipPatcher.cs index bb127747..493479c0 100644 --- a/Nautilus/Patchers/TooltipPatcher.cs +++ b/Nautilus/Patchers/TooltipPatcher.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Reflection; using System.Text; using BepInEx.Logging; @@ -12,6 +12,11 @@ internal class TooltipPatcher { internal static bool DisableEnumIsDefinedPatch = false; + // For compatibility purposes: + + private static MethodInfo _smlHelperIsVanillaTechTypeMethod; + private static MethodInfo _smlHelperWriteModNameFromTechTypeMethod; + internal static void Patch(Harmony harmony) { Initialize(); @@ -45,25 +50,82 @@ internal static void CustomTooltip(StringBuilder sb, TechType techType) WriteSpace(sb); } - if (techType.IsDefinedByDefault()) + // Compatibility for SMLHelper if it is installed + if (SMLHelperCompatibilityPatcher.SMLHelperInstalled) + { + var safeToUseSMLMethods = EnsureSMLHelperMethodReferences(); + + if (techType.IsDefinedByDefault()) + { + if (safeToUseSMLMethods && !(bool)_smlHelperIsVanillaTechTypeMethod.Invoke(null, new object[] { techType })) + { + _smlHelperWriteModNameFromTechTypeMethod.Invoke(null, new object[] { sb, techType }); + } + else + { +#if SUBNAUTICA + WriteModName(sb, "Subnautica"); +#elif BELOWZERO + WriteModName(sb, "BelowZero"); +#endif + } + } + else if (EnumHandler.TryGetOwnerAssembly(techType, out Assembly assembly)) + { + WriteModNameFromAssembly(sb, assembly); + } + else + { + WriteModNameError(sb, "Unknown Mod", "Item added without Nautilus"); + } + } + // Otherwise use a more basic method of doing things + else + { + if (techType.IsDefinedByDefault()) #if SUBNAUTICA - WriteModName(sb, "Subnautica"); + WriteModName(sb, "Subnautica"); #elif BELOWZERO WriteModName(sb, "BelowZero"); #endif - else if (EnumHandler.TryGetOwnerAssembly(techType, out Assembly assembly)) + else if (EnumHandler.TryGetOwnerAssembly(techType, out Assembly assembly)) + { + WriteModNameFromAssembly(sb, assembly); + } + else + { + WriteModNameError(sb, "Unknown Mod", "Item added without Nautilus"); + } + } + } + + // For SMLHelper compatibility only + private static bool EnsureSMLHelperMethodReferences() + { + if (_smlHelperIsVanillaTechTypeMethod == null) { - WriteModNameFromAssembly(sb, assembly); + _smlHelperIsVanillaTechTypeMethod = AccessTools.Method(SMLHelperCompatibilityPatcher.GetSMLType("SMLHelper.V2.Patchers.TooltipPatcher"), "IsVanillaTechType"); + if (_smlHelperIsVanillaTechTypeMethod == null) + { + InternalLogger.Error("Failed to locate SMLHelper's TooltipPatcher.IsVanillaTechType method!"); + return false; + } } - else + if (_smlHelperWriteModNameFromTechTypeMethod == null) { - WriteModNameError(sb, "Unknown Mod", "Item added without Nautilus"); + _smlHelperWriteModNameFromTechTypeMethod = AccessTools.Method(SMLHelperCompatibilityPatcher.GetSMLType("SMLHelper.V2.Patchers.TooltipPatcher"), "WriteModNameFromTechType"); + if (_smlHelperWriteModNameFromTechTypeMethod == null) + { + InternalLogger.Error("Failed to locate SMLHelper's TooltipPatcher.WriteModNameFromTechType method!"); + return false; + } } + return true; } - + internal static void WriteTechType(StringBuilder sb, TechType techType) { - sb.AppendFormat("\n\n{0} ({1})", techType.AsString(), (int)techType); + sb.AppendFormat("\n\n{0} ({1})", techType.AsString(), (int) techType); } internal static void WriteModName(StringBuilder sb, string text) { @@ -77,7 +139,7 @@ internal static void WriteModNameFromAssembly(StringBuilder sb, Assembly assembl { string modName = assembly.GetName().Name; - if(string.IsNullOrEmpty(modName)) + if (string.IsNullOrEmpty(modName)) { WriteModNameError(sb, "Unknown Mod", "Mod could not be determined"); } @@ -136,7 +198,7 @@ internal static void Initialize() } Initialized = true; - + var nautilusFolder = Path.Combine(BepInEx.Paths.ConfigPath, Assembly.GetExecutingAssembly().GetName().Name); Directory.CreateDirectory(nautilusFolder);